From cc19268132a53ae6990ae544babbf5840aa38092 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 12 Jun 2025 16:46:07 -0500 Subject: [PATCH] New version of interface for QBitMetaData production --- .../metadata/qbits/QBitMetaDataProducer.java | 192 ++++++++++++++++++ .../model/metadata/qbits/QBitProducer.java | 3 - .../metadata/qbits/QBitProductionContext.java | 136 +++++++++++++ 3 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitMetaDataProducer.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProductionContext.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitMetaDataProducer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitMetaDataProducer.java new file mode 100644 index 00000000..c37f466b --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitMetaDataProducer.java @@ -0,0 +1,192 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.qbits; + + +import java.util.List; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException; +import com.kingsrook.qqq.backend.core.logging.QLogger; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerOutput; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; +import com.kingsrook.qqq.backend.core.utils.StringUtils; +import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; + + +/******************************************************************************* + ** 2nd generation interface for top-level meta-data production classes that make + ** a qbit (evolution over QBitProducer). + ** + *******************************************************************************/ +public interface QBitMetaDataProducer extends MetaDataProducerInterface +{ + QLogger LOG = QLogger.getLogger(QBitMetaDataProducer.class); + + /*************************************************************************** + ** + ***************************************************************************/ + C getQBitConfig(); + + + /*************************************************************************** + ** + ***************************************************************************/ + QBitMetaData getQBitMetaData(); + + + /*************************************************************************** + ** + ***************************************************************************/ + default String getNamespace() + { + return (null); + } + + /*************************************************************************** + ** + ***************************************************************************/ + default void postProduceActions(MetaDataProducerMultiOutput metaDataProducerMultiOutput, QInstance qinstance) + { + ///////////////////// + // noop by default // + ///////////////////// + } + + /*************************************************************************** + ** + ***************************************************************************/ + default String getPackageNameForFindingMetaDataProducers() + { + Class clazz = getClass(); + + //////////////////////////////////////////////////////////////// + // Walk up the hierarchy until we find the direct implementer // + //////////////////////////////////////////////////////////////// + while(clazz != null) + { + Class[] interfaces = clazz.getInterfaces(); + for(Class interfaze : interfaces) + { + if(interfaze == QBitMetaDataProducer.class) + { + return clazz.getPackageName(); + } + } + clazz = clazz.getSuperclass(); + } + + throw (new QRuntimeException("Unable to find packageName for QBitMetaDataProducer. You may need to implement getPackageName yourself...")); + } + + + /*************************************************************************** + ** + ***************************************************************************/ + @Override + default MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException + { + MetaDataProducerMultiOutput rs = new MetaDataProducerMultiOutput(); + + QBitMetaData qBitMetaData = getQBitMetaData(); + C qBitConfig = getQBitConfig(); + + qInstance.addQBit(qBitMetaData); + + QBitProductionContext.pushQBitConfig(qBitConfig); + QBitProductionContext.pushMetaDataProducerMultiOutput(rs); + + try + { + qBitConfig.validate(qInstance); + + List> producers = MetaDataProducerHelper.findProducers(getPackageNameForFindingMetaDataProducers()); + MetaDataProducerHelper.sortMetaDataProducers(producers); + for(MetaDataProducerInterface producer : producers) + { + if(producer.getClass().equals(this.getClass())) + { + ///////////////////////////////////////////// + // avoid recursive processing of ourselves // + ///////////////////////////////////////////// + continue; + } + + //////////////////////////////////////////////////////////////////////////// + // todo is this deprecated in favor of QBitProductionContext's stack... ? // + //////////////////////////////////////////////////////////////////////////// + if(producer instanceof QBitComponentMetaDataProducer) + { + QBitComponentMetaDataProducer qBitComponentMetaDataProducer = (QBitComponentMetaDataProducer) producer; + qBitComponentMetaDataProducer.setQBitConfig(qBitConfig); + } + + if(!producer.isEnabled()) + { + LOG.debug("Not using producer which is not enabled", logPair("producer", producer.getClass().getSimpleName())); + continue; + } + + MetaDataProducerOutput subProducerOutput = producer.produce(qInstance); + + ///////////////////////////////////////////////// + // apply some things from the config to tables // + ///////////////////////////////////////////////// + if(subProducerOutput instanceof QTableMetaData table) + { + if(qBitConfig.getTableMetaDataCustomizer() != null) + { + subProducerOutput = qBitConfig.getTableMetaDataCustomizer().customizeMetaData(qInstance, table); + } + + if(!StringUtils.hasContent(table.getBackendName()) && StringUtils.hasContent(qBitConfig.getDefaultBackendNameForTables())) + { + table.setBackendName(qBitConfig.getDefaultBackendNameForTables()); + } + } + + //////////////////////////////////////////////////////////// + // set source qbit, if subProducerOutput is aware of such // + //////////////////////////////////////////////////////////// + if(subProducerOutput instanceof SourceQBitAware sourceQBitAware) + { + sourceQBitAware.setSourceQBitName(qBitMetaData.getName()); + } + + rs.add(subProducerOutput); + } + + postProduceActions(rs, qInstance); + + return (rs); + } + finally + { + QBitProductionContext.popQBitConfig(); + QBitProductionContext.popMetaDataProducerMultiOutput(); + } + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProducer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProducer.java index 7fef2174..4069c14d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProducer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProducer.java @@ -76,9 +76,6 @@ public interface QBitProducer { qBitConfig.validate(qInstance); - /////////////////////////////// - // todo - move to base class // - /////////////////////////////// for(MetaDataProducerInterface producer : producers) { if(producer instanceof QBitComponentMetaDataProducer) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProductionContext.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProductionContext.java new file mode 100644 index 00000000..5c699ba7 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/qbits/QBitProductionContext.java @@ -0,0 +1,136 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2025. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.qbits; + + +import java.util.Collections; +import java.util.List; +import java.util.Stack; +import com.kingsrook.qqq.backend.core.logging.QLogger; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput; + + +/******************************************************************************* + ** While a qbit is being produced, track the context of the current config + ** and metaDataProducerMultiOutput that is being used. also, in case one + ** qbit produces another, push these contextual objects on a stack. + *******************************************************************************/ +public class QBitProductionContext +{ + private static final QLogger LOG = QLogger.getLogger(QBitProductionContext.class); + + private static Stack qbitConfigStack = new Stack<>(); + private static Stack metaDataProducerMultiOutputStack = new Stack<>(); + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static void pushQBitConfig(QBitConfig qBitConfig) + { + qbitConfigStack.push(qBitConfig); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static QBitConfig peekQBitConfig() + { + if(qbitConfigStack.isEmpty()) + { + LOG.warn("Request to peek at empty QBitProductionContext configStack - returning null"); + return (null); + } + return qbitConfigStack.peek(); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static void popQBitConfig() + { + if(qbitConfigStack.isEmpty()) + { + LOG.warn("Request to pop empty QBitProductionContext configStack - returning with noop"); + return; + } + + qbitConfigStack.pop(); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static void pushMetaDataProducerMultiOutput(MetaDataProducerMultiOutput metaDataProducerMultiOutput) + { + metaDataProducerMultiOutputStack.push(metaDataProducerMultiOutput); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static MetaDataProducerMultiOutput peekMetaDataProducerMultiOutput() + { + if(metaDataProducerMultiOutputStack.isEmpty()) + { + LOG.warn("Request to peek at empty QBitProductionContext configStack - returning null"); + return (null); + } + return metaDataProducerMultiOutputStack.peek(); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static List getReadOnlyViewOfMetaDataProducerMultiOutputStack() + { + return Collections.unmodifiableList(metaDataProducerMultiOutputStack); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static void popMetaDataProducerMultiOutput() + { + if(metaDataProducerMultiOutputStack.isEmpty()) + { + LOG.warn("Request to pop empty QBitProductionContext metaDataProducerMultiOutput - returning with noop"); + return; + } + + metaDataProducerMultiOutputStack.pop(); + } + +}