diff --git a/src/cas/cas.cpp b/src/cas/cas.cpp index 521cc8f..6afc152 100644 --- a/src/cas/cas.cpp +++ b/src/cas/cas.cpp @@ -179,6 +179,7 @@ namespace uima { iv_indexRepository(NULL), iv_filterBuilder(NULL), iv_componentInfo(NULL), + iv_owner(NULL), iv_utDocumentType(uima::lowlevel::TypeSystem::INVALID_TYPE), iv_utDocumentLangAsIntFeat(uima::lowlevel::TypeSystem::INVALID_FEATURE), iv_utDocumentLangAsStrFeat(uima::lowlevel::TypeSystem::INVALID_FEATURE), @@ -221,6 +222,7 @@ namespace uima { iv_typeSystem = inCas->iv_typeSystem; iv_heap = inCas->iv_heap; iv_componentInfo = inCas->iv_componentInfo; + iv_owner = inCas->iv_owner; iv_utDocumentLangAsIntFeat = uima::lowlevel::TypeSystem::INVALID_FEATURE; iv_utDocumentLangAsStrFeat = uima::lowlevel::TypeSystem::INVALID_FEATURE; refreshCachedTypes(); @@ -262,6 +264,7 @@ namespace uima { iv_sofaCount(0), initialSofaCreated(false), iv_initialView(NULL), + iv_owner(NULL), iv_indexRepository(NULL), iv_filterBuilder(NULL), iv_componentInfo(NULL), @@ -676,6 +679,10 @@ namespace uima { ); } + void CAS::setOwner(AnnotatorContext *owner) { + iv_baseCas->iv_owner = owner; + } + // deprecated version void CAS::setDocumentText(UChar const * cpDocument, size_t uiLength, bool bCopyToCAS ) { if (cpDocument == NULL) { @@ -809,6 +816,21 @@ namespace uima { } } + void CAS::release() { + if (iv_baseCas->iv_owner) { + iv_baseCas->iv_owner->releaseCAS(*this); + } else { + ErrorMessage msg(UIMA_MSG_ID_EXC_INVALID_CAS_RELEASE); + msg.addParam("This CAS does not have any owner"); + UIMA_EXC_THROW_NEW(CASException, + UIMA_ERR_CAS_RELEASE, + msg, + ErrorMessage(UIMA_MSG_ID_EXCON_UNKNOWN_CONTEXT), + ErrorInfo::recoverable + ); + } + } + ANIndex CAS::getAnnotationIndex(Type const & crType) { if (isbaseCas) { assertWithMsg(false, "Annotation Index does not exist in Base CAS!"); diff --git a/src/cas/uima/cas.hpp b/src/cas/uima/cas.hpp index bd88b59..8de6fe7 100644 --- a/src/cas/uima/cas.hpp +++ b/src/cas/uima/cas.hpp @@ -191,6 +191,8 @@ namespace uima { void bumpSofaCount(); void invalidBaseCasMethod(); + /** Set the owner of the base CAS */ + void setOwner(AnnotatorContext* owner); void registerView(SofaFS); void updateDocumentAnnotation( ); void copyDocumentString(UnicodeStringRef); @@ -231,6 +233,7 @@ namespace uima { bool initialSofaCreated; bool isDeletingViews; //set this flag to true when destroying CAS AnnotatorContext *iv_componentInfo; + AnnotatorContext *iv_owner; uima::lowlevel::TyFSType iv_utDocumentType; uima::lowlevel::TyFSFeature iv_utDocumentLangAsIntFeat; @@ -772,6 +775,13 @@ namespace uima { icu::UnicodeString getAnnotationIndexID() const { return CAS::INDEXID_ANNOTATION; } + + /** + * When called this CAS will release itself by calling releaseCas on the AnnotatorContext that owns it. + * NOTE: This only works for CASes that have an owner, ie. belong to a CASPool. + */ + void release(); + /** @} */ /** @defgroup PreDefTypes Predefined Types diff --git a/src/framework/Makefile.am b/src/framework/Makefile.am index 3bdaf9e..b3490c9 100644 --- a/src/framework/Makefile.am +++ b/src/framework/Makefile.am @@ -78,7 +78,9 @@ libuima_la_SOURCES += consoleui.cpp libuima_la_SOURCES += cp2ucnvrt.cpp libuima_la_SOURCES += dottypesystemwriter.cpp libuima_la_SOURCES += engine.cpp -libuima_la_SOURCES += exceptions.cpp +libuima_la_SOURCES += exceptions.cpp +libuima_la_SOURCES += flow_controller.cpp +libuima_la_SOURCES += flow.cpp libuima_la_SOURCES += ftools.cpp libuima_la_SOURCES += internal_aggregate_engine.cpp libuima_la_SOURCES += internal_capability_container.cpp diff --git a/src/framework/annotator_context.cpp b/src/framework/annotator_context.cpp index 8e3dcc6..9a0b38e 100644 --- a/src/framework/annotator_context.cpp +++ b/src/framework/annotator_context.cpp @@ -165,7 +165,11 @@ namespace uima { } TyErrorId AnnotatorContext::defineCASPool(size_t numInstances) { - iv_pCasPool = new CASPool(getTaeSpecifier(),numInstances); + if (iv_pParentAnC) { // If this is a delegate + iv_pCasPool = new CASPool(this, iv_pParentAnC->getTaeSpecifier(),numInstances); + } else { + iv_pCasPool = new CASPool(this, getTaeSpecifier(), numInstances); + } if (iv_pCasPool == NULL) { return UIMA_ERR_USER_ANNOTATOR_OUT_OF_MEMORY; } @@ -500,7 +504,7 @@ namespace uima { NameValuePair const * AnnotatorContext::findNameValuePair(const icu::UnicodeString & paramName, const icu::UnicodeString & ancKey) const { /* return findNameValuePair(getGroupNameWhenNotSpec(), paramName, iv_pTaeSpecifier->getSearchStrategy()); */ - NameValuePair const * pValueLocal = pValueLocal = iv_pTaeSpecifier->getNameValuePair(paramName, ancKey); + NameValuePair const * pValueLocal = iv_pTaeSpecifier->getNameValuePair(paramName, ancKey); // the request was invalid we got an exception we leave to others to catch diff --git a/src/framework/annotator_mgr.cpp b/src/framework/annotator_mgr.cpp index 19aa35c..c970850 100644 --- a/src/framework/annotator_mgr.cpp +++ b/src/framework/annotator_mgr.cpp @@ -103,6 +103,8 @@ namespace uima { launchDeInit(); } assert( iv_vecEntries.empty() ); + if (iv_pFlowController) + delete iv_pFlowController; } @@ -152,12 +154,21 @@ namespace uima { assert(iv_vecEntries.empty()); AnnotatorContext & rANC = iv_pEngine->getAnnotatorContext(); - AnalysisEngineDescription const & crTAESpecifier = rANC.getTaeSpecifier(); // this method must be added - + const AnalysisEngineMetaData* pEngineMetadata = crTAESpecifier.getAnalysisEngineMetaData(); assert( ! crTAESpecifier.isPrimitive() ); - //BSIvector < icu::UnicodeString > const & crVecEngineNames = crTAESpecifier.getAnalysisEngineMetaData()->getFixedFlow()->getNodes(); - vector < icu::UnicodeString > const & crVecEngineNames = crTAESpecifier.getAnalysisEngineMetaData()->getFlowConstraints()->getNodes(); + + // FIXME: This shouldn't have been necessary since FlowContrainst::getFlowContraintsType + // should have been const in the first place + auto flowContraints = CONST_CAST(FlowConstraints *, pEngineMetadata->getFlowConstraints()); + if (flowContraints->getFlowConstraintsType() == FlowConstraints::FIXED) { + iv_pFlowController = new FixedFlowController; + iv_pFlowController->initialize(rANC); + } + + if (const OperationalProperties* operationalProps = pEngineMetadata->getOperationalProperties()) + iv_bOutputNewCases = operationalProps->getOutputsNewCASes(); + vector < icu::UnicodeString > const & crVecEngineNames = flowContraints->getNodes(); // for all engines in the flow size_t ui; @@ -431,11 +442,7 @@ namespace uima { return bHasTOF; } - - - - - TyErrorId AnnotatorManager::launchProcessDocument(CAS & cas, ResultSpecification const & crResultSpec) { + TyErrorId AnnotatorManager::processCapabilityLanguageFlow(CAS &cas, ResultSpecification const &crResultSpec) { /* This works as follows: The passes result spec is copied and for each delegate AE, it is determined @@ -486,27 +493,13 @@ namespace uima { bool requiresTCas=true; if (cas.isBackwardCompatibleCas()) { - tcas = &cas; - } - //this populates the tofsToBeRemoved vector so always call it - callEngine = shouldEngineBeCalled(*pCapContainer, - resSpec, - cas.getDocumentAnnotation().getLanguage(), - tofsToBeRemoved); - //check the FlowConstraintType specified in the aggregate engine - //if CapabilityLanguageFlow whether engine is called is - //determined by shouldEngineBeCalled() - AnnotatorContext & rANC = iv_pEngine->getAnnotatorContext(); - AnalysisEngineDescription const & crTAESpecifier = rANC.getTaeSpecifier(); - FlowConstraints const * pFlow = crTAESpecifier.getAnalysisEngineMetaData()->getFlowConstraints(); - FlowConstraints * flow = CONST_CAST(FlowConstraints *, pFlow); - FlowConstraints::EnFlowType flowType = flow->getFlowConstraintsType(); - - //if FixedFlow specified all engines are always called so reset callEngine is true - if (flowType == FlowConstraints::FIXED) { - callEngine=true; - } - + tcas = &cas; + } + //this populates the tofsToBeRemoved vector so always call it + callEngine = shouldEngineBeCalled(*pCapContainer, + resSpec, + cas.getDocumentAnnotation().getLanguage(), + tofsToBeRemoved); if ( callEngine ) { @@ -583,7 +576,186 @@ namespace uima { } UIMA_ANNOTATOR_TIMING(iv_clTimerLaunchProcess.stop()); - return(utRetVal); + return utRetVal; + } + + CAS *AnnotatorManager::processUntilNextOutputCas() { + unique_ptr flow{}; + while (true) { + CAS *currentCas = nullptr; + Step nextStep; + flow = nullptr; + + // get a cas from the stack + if (casIterStack.empty()) + return nullptr; + + StackFrame &frame = casIterStack.top(); + try { + if (frame.casMultiplier && frame.casMultiplier->hasNext()) { + currentCas = &frame.casMultiplier->next(); + // compute flow for newly produced CAS + flow = frame.originalFlow->newCasProduced(*currentCas, frame.lastEngineKey); + } + } catch (Exception &exception) { + if (!frame.originalFlow->continueOnFailure(frame.lastEngineKey /* ,exception */)) + throw; + } + + if (!currentCas) { + // if there is no more output CASes from the stack, take the original CAS that was processed by + // the CAS Multiplier and continue with its flow + currentCas = frame.originalCas; + flow = std::move(frame.originalFlow); + currentCas->setCurrentComponentInfo(nullptr); + casIterStack.pop(); + } + + activeCASes.insert(currentCas); + + if (nextStep.getType() == Step::StepType::UNSPECIFIED) { + nextStep = flow->next(); // get the next step for the current flow + } + + while (nextStep.getType() != Step::StepType::FINALSTEP) { + if (nextStep.getType() == Step::StepType::SIMPLESTEP) { + // find the AE specified by the step + const icu::UnicodeString &nextAEKey = nextStep.getSimpleStep()->getEngineName(); + auto it = std::find_if(iv_vecEntries.begin(), iv_vecEntries.end(), + [&, nextAEKey](const EngineEntry &entry) { + return entry.iv_pEngine->getAnnotatorContext().iv_AnCKey == nextAEKey; + }); + + if (it != iv_vecEntries.end()) { + AnalysisEngine *nextAE = it->iv_pEngine; + CAS *outputCas = nullptr; + + // call process one the AE and see if it has produced a new CAS + try { + CASIterator casIter = nextAE->processAndOutputNewCASes(*currentCas); + if (casIter.hasNext()) + outputCas = &casIter.next(); + } catch (Exception &e) { + if (!flow->continueOnFailure(nextAEKey)) + throw; + } + + if (outputCas) { + // new CAS is output so put the current components on the stack so we can process + // the other output CASes and original CASes later + std::unique_ptr nextFlow = flow->newCasProduced(*outputCas, nextAEKey); + casIterStack.push({nextAE, currentCas, std::move(flow), nextAEKey}); + flow = std::move(nextFlow); + currentCas = outputCas; + activeCASes.insert(currentCas); + } else { + // No new CASes are output, this CAS is done being processed by the current engine. + currentCas->setCurrentComponentInfo(nullptr); + } + } else { + UIMA_EXC_THROW_NEW(EngineProcessingException, + UIMA_ERR_USER_ANNOTATOR_COULD_NOT_PROCESS, + UIMA_MSG_ID_EXCON_PROCESSING_CAS, + ErrorMessage(UIMA_MSG_ID_LITERAL_STRING, "Unknown Delegate Key " + nextAEKey), + ErrorInfo::unrecoverable); + } + } else if (nextStep.getType() == Step::StepType::PARALLELSTEP) { + // TODO: ParallelStep not supported yet + UIMA_EXC_THROW_NEW(NotYetImplementedException, + UIMA_ERR_NOT_YET_IMPLEMENTED, + UIMA_MSG_ID_EXC_NOT_YET_IMPLEMENTED, + ErrorMessage(UIMA_MSG_ID_LITERAL_STRING, "Parallel Step not supported yet"), + ErrorInfo::unrecoverable + ); + } else { + UIMA_EXC_THROW_NEW(EngineProcessingException, + UIMA_ERR_USER_ANNOTATOR_COULD_NOT_PROCESS, + UIMA_MSG_ID_EXCON_PROCESSING_CAS, + ErrorMessage(UIMA_MSG_ID_LITERAL_STRING, "Unknown Step Type"), + ErrorInfo::unrecoverable); + } + + nextStep = flow->next(); + } + + const FinalStep *finalStep = nextStep.getFinalStep(); + activeCASes.erase(currentCas); + if (currentCas == inputCas) { + if (finalStep->getForceDropCAS()) { + // Not allowed to drop the input CAS so something must have gone wrong + UIMA_EXC_THROW_NEW(EngineProcessingException, + UIMA_ERR_USER_ANNOTATOR_COULD_NOT_PROCESS, + UIMA_MSG_ID_EXCON_PROCESSING_CAS, + ErrorMessage(UIMA_MSG_ID_LITERAL_STRING, "Illegal CAS drop"), + ErrorInfo::unrecoverable); + } + return nullptr; + } + + if (iv_bOutputNewCases && !finalStep->getForceDropCAS()) + return currentCas; + currentCas->release(); + } + } + + bool AnnotatorManager::hasNext() { + if (!nextCas) + nextCas = processUntilNextOutputCas(); + return nextCas != nullptr; + } + + CAS & AnnotatorManager::next() { + CAS* result = nextCas; + if (!result) + result = processUntilNextOutputCas(); + if (!result) { + UIMA_EXC_THROW_NEW(Exception, + UIMA_ERR_USER_ANNOTATOR_COULD_NOT_PROCESS, + UIMA_MSG_ID_EXCON_PROCESSING_CAS, + ErrorMessage(UIMA_MSG_ID_LITERAL_STRING, "There is not next() available."), + ErrorInfo::unrecoverable); + } + nextCas = nullptr; + return *result; + } + + void AnnotatorManager::release() { + while (!casIterStack.empty()) { + StackFrame& frame = casIterStack.top(); + frame.originalFlow->aborted(); + casIterStack.pop(); + } + for (CAS *cas : activeCASes) { + if (cas != inputCas) + cas->release(); + } + + activeCASes.clear(); + } + + + TyErrorId AnnotatorManager::launchProcessDocument(CAS &cas, ResultSpecification const &crResultSpec) { + //if engine uses Capability Language Flow + // TODO: Turn this logic and processCapabilityLanguageFlow into a separate CapabilityLanguageFlowController class that inherits from FlowController + AnnotatorContext &rANC = iv_pEngine->getAnnotatorContext(); + AnalysisEngineDescription const &crTAESpecifier = rANC.getTaeSpecifier(); + FlowConstraints const *pFlow = crTAESpecifier.getAnalysisEngineMetaData()->getFlowConstraints(); + FlowConstraints *flow = CONST_CAST(FlowConstraints *, pFlow); + + // Process according to capability language specifications + if (flow->getFlowConstraintsType() == FlowConstraints::CAPABILITYLANGUAGE) + return processCapabilityLanguageFlow(cas, crResultSpec); + + inputCas = &cas; + + casIterStack.push({nullptr, inputCas, iv_pFlowController->computeFlow(*inputCas), {}}); + try { + nextCas = processUntilNextOutputCas(); + } catch (...) { + release(); + throw; + } + return UIMA_ERR_NONE; } #ifdef UIMA_DEBUG_ANNOTATOR_TIMING diff --git a/src/framework/caspool.cpp b/src/framework/caspool.cpp index 78e966e..0bddab9 100644 --- a/src/framework/caspool.cpp +++ b/src/framework/caspool.cpp @@ -31,6 +31,8 @@ #include "uima/caspool.hpp" + +#include "uima/annotator_context.hpp" #include "uima/err_ids.h" #include "uima/msg.h" @@ -45,16 +47,14 @@ namespace uima { // //------------------------------------------------------------ - CASPool::CASPool(const AnalysisEngineDescription & taeSpec, - size_t numInstances) - :iv_vecAllInstances(), - iv_vecFreeInstances(), - iv_pCasDef(NULL), - iv_numInstances(numInstances) { - - iv_pCasDef = uima::internal::CASDefinition::createCASDefinition(taeSpec); - - if (iv_pCasDef == NULL) { + CASPool::CASPool(AnnotatorContext *anContext, const AnalysisEngineDescription &taeSpec, + size_t numInstances) : iv_vecAllInstances(), + iv_vecFreeInstances(), + iv_numInstances(numInstances), + iv_pCasDef(nullptr), + iv_pOwner(anContext) { + iv_pCasDef = internal::CASDefinition::createCASDefinition(taeSpec); + if (iv_pCasDef == nullptr) { UIMA_EXC_THROW_NEW(CASPoolException, UIMA_ERR_CASPOOL_CREATE_CASDEFINITION, UIMA_MSG_ID_EXC_CREATE_CASPOOL, @@ -62,17 +62,19 @@ namespace uima { ErrorInfo::unrecoverable); } - for (size_t i=0; i < numInstances; i++) { - CAS * pCas = uima::internal::CASImpl::createCASImpl(*iv_pCasDef,false); - if (pCas == NULL) { + for (size_t i = 0; i < numInstances; i++) { + CAS *pCas = uima::internal::CASImpl::createCASImpl(*iv_pCasDef, false); + if (pCas == nullptr) { UIMA_EXC_THROW_NEW(CASPoolException, UIMA_ERR_CASPOOL_CREATE_CAS, UIMA_MSG_ID_EXC_CREATE_CASPOOL, UIMA_MSG_ID_EXC_CREATE_CASPOOL, ErrorInfo::unrecoverable); } - iv_vecAllInstances.push_back((CAS *)pCas->getInitialView()); - iv_vecFreeInstances.push_back((CAS *)pCas->getInitialView()); + pCas->setOwner(iv_pOwner); + CAS *initialView = pCas->getInitialView(); + iv_vecAllInstances.push_back(initialView); + iv_vecFreeInstances.push_back(initialView); } } @@ -112,10 +114,17 @@ namespace uima { } void CASPool::releaseCAS(CAS & aCas) { - + if (std::find(iv_vecAllInstances.begin(), iv_vecAllInstances.end(), &aCas) == iv_vecAllInstances.end()) { + ErrorMessage msg(UIMA_MSG_ID_EXC_INVALID_CAS_RELEASE); + msg.addParam("This CAS does not belong to this CAS Pool"); + UIMA_EXC_THROW_NEW(CASPoolException, + UIMA_ERR_CAS_RELEASE, + msg, + ErrorMessage(UIMA_MSG_ID_EXCON_UNKNOWN_CONTEXT), + ErrorInfo::recoverable); + } aCas.reset(); iv_vecFreeInstances.push_back(&aCas); - return; } } //namespace diff --git a/src/framework/flow.cpp b/src/framework/flow.cpp new file mode 100644 index 0000000..b230aaa --- /dev/null +++ b/src/framework/flow.cpp @@ -0,0 +1,132 @@ +/** \file flow_controller.cpp . +----------------------------------------------------------------------------- + + + + + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + +----------------------------------------------------------------------------- +----------------------------------------------------------------------------- + + +-------------------------------------------------------------------------- */ + +#include "uima/flow.hpp" + +namespace uima { + Step::Step(const internal::SimpleStep &simpleStep): uStep(simpleStep), + type(StepType::SIMPLESTEP) { + } + + Step::Step(const internal::ParallelStep ¶llelStep): uStep(parallelStep), + type(StepType::PARALLELSTEP) { + } + + Step::Step(const internal::FinalStep &finalStep): uStep(finalStep), + type(StepType::FINALSTEP) { + } + + Step::Step(const Step& other) :type(other.type) { + // properly initialize union value that was not constructed (invalid) + switch (other.type) { + case StepType::SIMPLESTEP: + new (&uStep.simpleStep) auto(other.uStep.simpleStep); + break; + case StepType::FINALSTEP: + new (&uStep.finalStep) auto(other.uStep.finalStep); + break; + case StepType::PARALLELSTEP: + new (&uStep.parallelStep) auto(other.uStep.parallelStep); + default: + break; + } + + } + + Step & Step::operator=(const Step &other) { + if (this == &other) { + return *this; + } + // destroy the current type in the union and reinitialize it with other + switch (this->type) { + case StepType::SIMPLESTEP: + uStep.simpleStep.~SimpleStep(); + break; + case StepType::FINALSTEP: + uStep.finalStep.~FinalStep(); + break; + case StepType::PARALLELSTEP: + uStep.parallelStep.~ParallelStep(); + break; + default: + break; + } + + type = other.type; + switch (other.type) { + case StepType::SIMPLESTEP: + new (&uStep.simpleStep) auto(other.uStep.simpleStep); + break; + case StepType::FINALSTEP: + new (&uStep.finalStep) auto(other.uStep.finalStep); + break; + case StepType::PARALLELSTEP: + new (&uStep.parallelStep) auto(other.uStep.parallelStep); + break; + default: + break; + } + return *this; + } + + Step::~Step() { + switch (type) { + case StepType::SIMPLESTEP: + uStep.simpleStep.~SimpleStep(); + break; + case StepType::FINALSTEP: + uStep.finalStep.~FinalStep(); + break; + case StepType::PARALLELSTEP: + uStep.parallelStep.~ParallelStep(); + break; + default: + break; + } + } + + const internal::SimpleStep * Step::getSimpleStep() const { + if (type != StepType::SIMPLESTEP) return nullptr; + return &uStep.simpleStep; + } + + const internal::ParallelStep * Step::getParallelStep() const { + if (type != StepType::PARALLELSTEP) return nullptr; + return &uStep.parallelStep; + } + + const internal::FinalStep * Step::getFinalStep() const { + if (type != StepType::FINALSTEP) return nullptr; + return &uStep.finalStep; + } + + Step::StepType Step::getType() const { + return type; + } +} diff --git a/src/framework/flow_controller.cpp b/src/framework/flow_controller.cpp new file mode 100644 index 0000000..3f800c0 --- /dev/null +++ b/src/framework/flow_controller.cpp @@ -0,0 +1,108 @@ +/** \file flow_controller.cpp . +----------------------------------------------------------------------------- + + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + +----------------------------------------------------------------------------- + + Description: Implementation of FixedFlowController and FixedFlowObject + +----------------------------------------------------------------------------- */ + + +#include "uima/flow_controller.hpp" + +namespace uima { + Step FixedFlowObject::next() { + // if this CAS had been passed to a CAS Multiplier in the previous step + if (wasPassedToCASMultiplier) { + switch (flowController->getAction()) { + case FixedFlowController::ActionAfterCasMultiplier::CONTINUE: + break; + case FixedFlowController::ActionAfterCasMultiplier::STOP: + return Step(internal::FinalStep()); + case FixedFlowController::ActionAfterCasMultiplier::DROP: + return Step(internal::FinalStep(internallyCreatedCAS)); + case FixedFlowController::ActionAfterCasMultiplier::DROP_IF_NEW_CAS_PRODUCED: + if (newCASProduced) + return Step(internal::FinalStep(internallyCreatedCAS)); + break; + } + wasPassedToCASMultiplier = newCASProduced = false; + } + + const std::vector& delegateKeys = flowController->getDelegateKeys(); + if (currentStep >= delegateKeys.size()) // this CAS has finished the sequence + return Step(internal::FinalStep()); + + // if the engine is a CAS Multiplier, set flag + const icu::UnicodeString &engineName = delegateKeys[currentStep]; + const AnnotatorContext* engineContext = flowController->getDelegateSpecifierMap().at(engineName); + const AnalysisEngineMetaData* engineMetadata = engineContext->getTaeSpecifier().getAnalysisEngineMetaData(); + const OperationalProperties* operationalProps = engineMetadata->getOperationalProperties(); + if ( operationalProps && operationalProps->getOutputsNewCASes() ) + wasPassedToCASMultiplier = true; + + return Step(internal::SimpleStep(delegateKeys[currentStep++])); + } + + std::unique_ptr FixedFlowObject::newCasProduced(const CAS &cas, const icu::UnicodeString &producedBy) { + newCASProduced = true; //input CAS has been processed by a CAS Multiplier + + // start the new output CAS from the next node after the CAS Multiplier that produced it + const std::vector& delegateKeys = flowController->getDelegateKeys(); + int i = 0; + while (producedBy != delegateKeys.at(i)) + ++i; + return std::make_unique(flowController, i+1, true); + } + +/* -------------------------------------------------------------------------------------------------------------- */ +/* FixedFlowController implementation */ +/* -------------------------------------------------------------------------------------------------------------- */ + void FixedFlowController::initialize(const AnnotatorContext &anContext) { + annotatorContext = &anContext; + delegateSpecifierMap = &anContext.getDelegates(); + flowContraints = anContext.getTaeSpecifier().getAnalysisEngineMetaData()->getFixedFlow(); + } + + void FixedFlowController::destroy() { + } + + void FixedFlowController::reconfigure() { + if (annotatorContext) + initialize(*annotatorContext); + } + + std::unique_ptr FixedFlowController::computeFlow(CAS&) { + return std::make_unique(this, 0); + } + + const std::vector & FixedFlowController::getDelegateKeys() const { + return flowContraints->getNodes(); + } + + const std::map & FixedFlowController::getDelegateSpecifierMap() const { + return *delegateSpecifierMap; + } + + FixedFlowController::ActionAfterCasMultiplier FixedFlowController::getAction() const { + return action; + } + +} diff --git a/src/framework/internal_aggregate_engine.cpp b/src/framework/internal_aggregate_engine.cpp index c0ca218..746c57a 100644 --- a/src/framework/internal_aggregate_engine.cpp +++ b/src/framework/internal_aggregate_engine.cpp @@ -220,15 +220,11 @@ namespace uima { } bool AggregateEngine::hasNextImpl() { - return false; + return iv_annotatorMgr.hasNext(); } CAS & AggregateEngine::nextImpl() { - UIMA_EXC_THROW_NEW(ExcInvalidRequest, - UIMA_ERR_NOT_YET_IMPLEMENTED, - UIMA_MSG_ID_EXC_INVALID_CALL_TO_NEXT, - UIMA_MSG_ID_EXC_INVALID_CALL_TO_NEXT, - ErrorInfo::unrecoverable); + return iv_annotatorMgr.next(); } int AggregateEngine::getCasInstancesRequiredImpl() { diff --git a/src/framework/internal_engine_base.cpp b/src/framework/internal_engine_base.cpp index 42f9f58..4327cc9 100644 --- a/src/framework/internal_engine_base.cpp +++ b/src/framework/internal_engine_base.cpp @@ -63,6 +63,8 @@ /* Implementation */ /* ----------------------------------------------------------------------- */ namespace uima { + UIMA_EXC_CLASSIMPLEMENT(EngineProcessingException, uima::Exception); + namespace internal { uima::internal::EngineBase & EngineBase::promoteEngine( uima::AnalysisEngine & engine) { diff --git a/src/framework/uima/annotator_context.hpp b/src/framework/uima/annotator_context.hpp index 5b63687..c1ed4a1 100644 --- a/src/framework/uima/annotator_context.hpp +++ b/src/framework/uima/annotator_context.hpp @@ -383,6 +383,8 @@ namespace uima { //AnnotatorContext(void); private: friend class uima::internal::EngineBase; + friend class CAS; + friend class internal::AnnotatorManager; AnalysisEngineDescription * getTAESpec() { return iv_pTaeSpecifier; diff --git a/src/framework/uima/annotator_mgr.hpp b/src/framework/uima/annotator_mgr.hpp index 9519f58..be9ed1b 100644 --- a/src/framework/uima/annotator_mgr.hpp +++ b/src/framework/uima/annotator_mgr.hpp @@ -31,7 +31,7 @@ 4/26/1999 Initial creation 1/17/2000 Autom. priorisation of annotators added - + 8/20/2024 CAS Multiplier capabilites added -------------------------------------------------------------------------- */ #ifndef UIMA_ANNOTATOR_MGR_HPP @@ -43,12 +43,15 @@ #include "uima/pragmas.hpp" //must be included first to disable warnings #include +#include +#include #include "uima/annotator_timing.hpp" #include "uima/exceptions.hpp" #include "uima/timedatetools.hpp" #include "uima/result_specification.hpp" +#include "uima/flow_controller.hpp" //#include "uima/internal_capability_container.hpp" /* ----------------------------------------------------------------------- */ @@ -204,6 +207,8 @@ namespace uima { protected: /* --- functions --- */ private: + friend class PrimitiveEngine; + friend class AggregateEngine; #ifdef UIMA_COMP_REQ_PUBLIC_TYPES public: #endif @@ -212,17 +217,34 @@ namespace uima { internal::CapabilityContainer * iv_pCapabilityContainer; } EngineEntry; + + struct StackFrame { + /* The delegate engine that produced new CASes */ + AnalysisEngine* casMultiplier; + /* The CAS that was input to the CAS Multiplier */ + CAS* originalCas; + /* The Flow object for this CAS */ + std::unique_ptr originalFlow; + /* The delegate key of the engine that produced new CASes */ + icu::UnicodeString lastEngineKey; + }; /* --- types --- */ typedef std::vector < EngineEntry > TyAnnotatorEntries; - private: - friend class uima::internal::PrimitiveEngine; - // the engine whic howns this annotator manager - internal::AggregateEngine * iv_pEngine; + // the engine which owns this annotator manager + AggregateEngine * iv_pEngine; /* --- variables --- */ - TyAnnotatorEntries iv_vecEntries; - bool iv_bIsInitialized; - size_t iv_uiNbrOfDocsProcessed; // for timing statistics - + TyAnnotatorEntries iv_vecEntries; + std::stack casIterStack; + + /** Active CASes during processing, released during exception handling*/ + std::unordered_set activeCASes; + + size_t iv_uiNbrOfDocsProcessed; // for timing statistics + FlowController* iv_pFlowController; + CAS* inputCas{}; + CAS* nextCas{}; + bool iv_bIsInitialized; + bool iv_bOutputNewCases; /* --- functions --- */ #ifdef UIMA_DEBUG_ANNOTATOR_TIMING Timer iv_clTimerLaunchInit; @@ -243,6 +265,21 @@ namespace uima { Language const &, std::vector&) ; + /** Helper method that handles the input CAS for Capability Language Flow */ + TyErrorId processCapabilityLanguageFlow(CAS &cas, ResultSpecification const &crResultSpec); + + /** This runs the aggregate engine from the current state until a new CAS is output */ + CAS* processUntilNextOutputCas(); + + /** Called by Aggregate Engine's hasNext */ + bool hasNext(); + + /** Called by Aggregate Engine's next */ + CAS& next(); + + /** Release all CASes currently in use by this */ + void release(); + /* COPY CONSTRUCTOR NOT SUPPORTED */ AnnotatorManager(const AnnotatorManager & ); //lint !e1704 /* ASSIGNMENT OPERATOR NOT SUPPORTED */ diff --git a/src/framework/uima/caspool.hpp b/src/framework/uima/caspool.hpp index 208b1ca..cdb19d2 100644 --- a/src/framework/uima/caspool.hpp +++ b/src/framework/uima/caspool.hpp @@ -67,15 +67,15 @@ namespace uima { std::vector iv_vecFreeInstances; size_t iv_numInstances; uima::internal::CASDefinition * iv_pCasDef; - + AnnotatorContext* iv_pOwner; public: /** Constructor - * Creates the specified number of CAS instances based on CAS definition - * as specified in the TAE specifier. + * @param anContext The AnnotatorContext that owns this CASPool + * @param taeSpec The AnalysisEngineDescription that specifies CAS Definition for this Pool + * @param numInstances Number of CASes in this Pool */ - CASPool(const AnalysisEngineDescription & taeSpec, size_t numInstances); - + CASPool(AnnotatorContext* anContext, const AnalysisEngineDescription & taeSpec, size_t numInstances); /** Destructor */ ~CASPool(void); diff --git a/src/framework/uima/err_ids.h b/src/framework/uima/err_ids.h index 421e00a..99aaf00 100644 --- a/src/framework/uima/err_ids.h +++ b/src/framework/uima/err_ids.h @@ -993,6 +993,11 @@ namespace uima { { UIMA_ERR_CODEPAGE , _TEXT("UIMA_ERR_CASPOOL_GET_CAS") }, #endif + /** CAS release errors */ +#define UIMA_ERR_CAS_RELEASE ((uima::TyErrorId)( 71 + UIMA_ERR_ENGINE_OFFSET )) +#ifdef UIMA_ENGINE_MAIN_CPP + { UIMA_ERR_CODEPAGE , _TEXT("UIMA_ERR_CAS_RELEASE") }, +#endif /*@}*/ diff --git a/src/framework/uima/flow.hpp b/src/framework/uima/flow.hpp new file mode 100644 index 0000000..4e857c2 --- /dev/null +++ b/src/framework/uima/flow.hpp @@ -0,0 +1,207 @@ +#ifndef UIMA_FLOW_HPP +#define UIMA_FLOW_HPP + +/** \file flow.hpp . +----------------------------------------------------------------------------- + + + + + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + +----------------------------------------------------------------------------- + + Description: Flow interface for the FlowController + +----------------------------------------------------------------------------- + + +-------------------------------------------------------------------------- */ +#include + +#include "uima/pragmas.hpp" +#include "uima/annotator.hpp" + +namespace uima { + namespace internal { + /** + * Indicates that a CAS should be routed to a single AnalysisEngine. + */ + class UIMA_LINK_IMPORTSPEC SimpleStep { + /* The key of the engine the CAS will be input to. + Not to be confused with the engine name, which is specified by the its descriptor*/ + icu::UnicodeString engineKey; + /* ResultSpecification *resultSpec; */ + public: + /* If SimpleStepWithResultSpec is required when CapabilityLanguageFlowController is implemented, + * this class could have an extra member ResultSpecification + SimpleStep(const icu::UnicodeString &name, ResultSpecification *resultSpec) : engineKey(name) { + } */ + + SimpleStep(const icu::UnicodeString &name) : engineKey(name) { + } + + const icu::UnicodeString &getEngineName() const { + return engineKey; + } + + /** + bool hasResultSpec() const { + return resultSpec != nullptr; + } + + ResultSpecification* getResultSpec() const { + return resultSpec; + } + */ + }; + + /** + * Not yet implemented + */ + class UIMA_LINK_IMPORTSPEC ParallelStep { + public: + ParallelStep() { + UIMA_EXC_THROW_NEW(NotYetImplementedException, + UIMA_ERR_NOT_YET_IMPLEMENTED, + UIMA_MSG_ID_EXC_NOT_YET_IMPLEMENTED, + ErrorMessage(UIMA_MSG_ID_EXCON_UNKNOWN_CONTEXT), + ErrorInfo::unrecoverable + ); + } + }; + + /* Indicates that a CAS has finished being processed by the aggregate. + */ + class UIMA_LINK_IMPORTSPEC FinalStep { + /* Whether the CAS should be dropped. Should only be true for CASes produced internally by the aggregate. + */ + bool forceDropCAS; + + public: + FinalStep() : forceDropCAS(false) { + } + + explicit FinalStep(bool forceDropCAS) : forceDropCAS(forceDropCAS) { + } + + bool getForceDropCAS() const { + return forceDropCAS; + } + }; + } + + + /** + * Class Step indicates where to route the current CAS to next. + * It is a union type of possible step types: Simple Step, Parallel Step and Final Step. + * It is returned using internal::Flow::next + */ + class UIMA_LINK_IMPORTSPEC Step { + public: + enum class StepType { SIMPLESTEP, FINALSTEP, PARALLELSTEP, UNSPECIFIED }; + + Step() : type(StepType::UNSPECIFIED) { + } + + explicit Step(const internal::SimpleStep &simpleStep); + + explicit Step(const internal::ParallelStep ¶llelStep); + + explicit Step(const internal::FinalStep &finalStep); + + Step(const Step &other); + + Step& operator=(const Step& other); + + ~Step(); + + const internal::SimpleStep *getSimpleStep() const; + + const internal::ParallelStep *getParallelStep() const; + + const internal::FinalStep *getFinalStep() const; + + StepType getType() const; + private: + union step_union { + internal::SimpleStep simpleStep; + internal::ParallelStep parallelStep; + internal::FinalStep finalStep; + + step_union(const internal::SimpleStep &simpleStep) : simpleStep(simpleStep) { + } + + step_union(const internal::ParallelStep ¶llelStep) : parallelStep(parallelStep) { + } + + step_union(const internal::FinalStep &finalStep) : finalStep(finalStep) { + } + step_union() { } + ~step_union() { } + } uStep; + + StepType type; + }; + + + /** + * Base class for the Flow objects computed by the FlowController. + * Flow objects are responsible for routing a CAS through an Aggregate Engine by returning a Step + * @see FlowController::computeFlow + */ + class UIMA_LINK_IMPORTSPEC Flow { + /** The CAS that this Flow object is handling. The Flow object can choose to use this method or not */ + CAS* inputCAS{nullptr}; + public: + virtual ~Flow(){}; + + /** + * Specify the next destination for the CAS via a Step object + */ + virtual Step next()=0; + + /** + * This method is called by the framework if this Flow's CAS has been sent to a CAS Multiplier that has created + * a new output CAS. It may throw an exception if the Engine does not support CAS Multipliers. + * @param cas the new CAS that has been produced + * @param producedBy the key of the delegate engine that has produced this CAS + * @return a new Flow object that will route the output CAS + */ + virtual std::unique_ptr newCasProduced(const CAS& cas, const icu::UnicodeString& producedBy)=0; + + /** + * Called by the framework after a failure to see if the CAS should continue or not. + * @param failedEngine the key of the engine whose failure led to this call + * TODO: include the offending exception as a parameter? + * @return whether processing should continue or be aborted + */ + virtual bool continueOnFailure(const icu::UnicodeString& failedEngine) { return false; } + + /** + * Called by the framework to alert this Flow object that processing has been stopped on this CAS so it can perform any cleanup + */ + virtual void aborted() { } + + void setCas(CAS* cas) { inputCAS = cas; } + CAS* getCas() const { return inputCAS; } + }; + + +} +#endif //UIMA_FLOW_HPP diff --git a/src/framework/uima/flow_controller.hpp b/src/framework/uima/flow_controller.hpp new file mode 100644 index 0000000..477d069 --- /dev/null +++ b/src/framework/uima/flow_controller.hpp @@ -0,0 +1,163 @@ +#ifndef UIMA_FLOW_CONTROLLER_HPP +#define UIMA_FLOW_CONTROLLER_HPP + +/** \file flow_controller.hpp . +----------------------------------------------------------------------------- + + + + + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + +----------------------------------------------------------------------------- + + Description: This file contains the FlowController class and Flow interface + that control the flow of CASes inside an Aggregate Analysis Engine + +----------------------------------------------------------------------------- + + 7/18/2024: created +-------------------------------------------------------------------------- */ + +#include "uima/annotator_context.hpp" +#include "uima/flow.hpp" +#include "uima/pragmas.hpp" +#include "uima/engine.hpp" + +namespace uima { + class FixedFlowController; + + /** + * A FlowController dictates how CASes are routed within Aggregate Analysis Engines. + *

+ * For each new CAS that is passed to the Aggregate Analysis Engine containing the FlowController, + * FlowController::computeFlow will be called. This method must return a Flow object that is responsible for + * routing that CAS through the components of the Aggregate Analysis Engine. + *

+ */ + class UIMA_LINK_IMPORTSPEC FlowController { + public: + virtual ~FlowController() = default; + + /** + * Initializes this Flow Controller and any related members. + * @param anContext + * Description of the AnalysisEngine that this Flow Controller belongs to. + */ + virtual void initialize(const AnnotatorContext& anContext)=0; + + /** Deinitialize this Flow Controller */ + virtual void destroy()=0; + + /** Reconfigure this Flow Controller */ + virtual void reconfigure()=0; + + /** + * Computes and returns a Flow object will the input CAS through the Aggregate. The + * Flow object should be given a handle to the CAS, so that it can use information in + * the CAS to make routing decisions. + * FlowController implementations can define their own class that implements Flow. + * @param cas A CAS that this FlowController should process. + * @return a Flow object that has responsibility for routing cas through the + * Aggregate Analysis Engine. + */ + virtual std::unique_ptr computeFlow(CAS &cas)=0; + }; + + + /** + * This class represents the Flow object used in a Fixed Flow Controller + */ + class UIMA_LINK_IMPORTSPEC FixedFlowObject : public Flow { + public: + FixedFlowObject(FixedFlowController *const flowController, int startStep, + bool internallyCreatedCAS = false) : flowController(flowController), currentStep(startStep), + wasPassedToCASMultiplier(false), + newCASProduced(false), + internallyCreatedCAS(internallyCreatedCAS) { + assert(EXISTS(flowController)); + } + + Step next() override; + + std::unique_ptr newCasProduced(const CAS& cas, const icu::UnicodeString& producedBy) override; + + private: + /** The Flow Controller that defines this Flow */ + FixedFlowController* flowController; + + /** Index of the delegate sequence this Flow is at*/ + int currentStep; + + /** Whether this flow's CAS was passed to a CAS Multiplier*/ + bool wasPassedToCASMultiplier; + + /** Whether this flow's CAS has produced a new CAS */ + bool newCASProduced; + + /** Whether this flow's CAS was produced internally by a CAS Multiplier */ + bool internallyCreatedCAS; + }; + + + class UIMA_LINK_IMPORTSPEC FixedFlowController : public FlowController { + public: + enum class ActionAfterCasMultiplier { CONTINUE, STOP, DROP, DROP_IF_NEW_CAS_PRODUCED }; + + FixedFlowController() : delegateSpecifierMap(), flowContraints(), annotatorContext(), + action(ActionAfterCasMultiplier::DROP_IF_NEW_CAS_PRODUCED) { + } + + void initialize(const AnnotatorContext &anContext) override; + + void destroy() override; + + void reconfigure() override; + + std::unique_ptr computeFlow(CAS &) override; + + const std::vector& getDelegateKeys() const; + + const std::map& getDelegateSpecifierMap() const; + + ActionAfterCasMultiplier getAction() const; + + private: + /** Maps from delegate engine keys (not names) to their corresponding AnnotatorContexts */ + const std::map* delegateSpecifierMap; + + /** The FlowContraints object that defines this FlowController*/ + const FixedFlow* flowContraints; + + /** The AnnotatorContext of the aggregate engine that owns this FlowController */ + const AnnotatorContext* annotatorContext; + + /** The action to be taken after a CAS has been input to a CAS Multiplier. For now this cannot be overridden yet.\n + * Values include:\n + * - CONTINUE: the CAS will continue with the flow\n + * - STOP: the CAS will not continue with the flow and be returned\n + * - DROP: the CAS will not continue and be dropped\n + * - DROP_IF_NEW_CAS_PRODUCED (default): If the CAS Multiplier produced a new CAS from this input CAS then this CAS will + * be dropped, otherwise it will continue. + */ + ActionAfterCasMultiplier action; + }; + +} + +#endif //UIMA_FLOW_CONTROLLER_HPP diff --git a/src/framework/uima/internal_engine_base.hpp b/src/framework/uima/internal_engine_base.hpp index 6e6df59..41f3d7d 100644 --- a/src/framework/uima/internal_engine_base.hpp +++ b/src/framework/uima/internal_engine_base.hpp @@ -64,6 +64,10 @@ namespace uima { /* Types / Classes */ /* ----------------------------------------------------------------------- */ namespace uima { + + /** Represents an exception relating to an engine's processing */ + UIMA_EXC_CLASSDECLARE(EngineProcessingException, uima::Exception); + namespace internal { /** diff --git a/src/framework/uima/msg.h b/src/framework/uima/msg.h index 785154d..1a1ecca 100644 --- a/src/framework/uima/msg.h +++ b/src/framework/uima/msg.h @@ -366,4 +366,5 @@ #define UIMA_MSG_ID_EXC_NO_FREE_CAS 329 #define UIMA_MSG_ID_EXC_INVALID_CALL_TO_NEXT 330 #define UIMA_MSG_ID_SIGNATURE_END 331 +#define UIMA_MSG_ID_EXC_INVALID_CAS_RELEASE 332 #endif diff --git a/src/framework/uima/msgstrtab.h b/src/framework/uima/msgstrtab.h index b5854fd..db6e152 100644 --- a/src/framework/uima/msgstrtab.h +++ b/src/framework/uima/msgstrtab.h @@ -702,6 +702,8 @@ static const TCHAR * gs_aszMessageStringTable[] = { "Invalid call to next(). ", /* 331 - UIMA_MSG_ID_SIGNATURE_END: */ "[UIMA-LIBRARY]", + /* 332 - UIMA_MSG_ID_EXC_INVALID_CAS_RELEASE */ + "Invalid release of CAS." } ; #endif /* UIMA_MSGSTRTAB_H */ diff --git a/src/test/data/descriptors/DaveDetector.xml b/src/test/data/descriptors/DaveDetector.xml index 79a765f..3a50ec5 100644 --- a/src/test/data/descriptors/DaveDetector.xml +++ b/src/test/data/descriptors/DaveDetector.xml @@ -24,7 +24,7 @@ org.apache.uima.cpp true -DaveDetector +libDaveDetector diff --git a/src/test/data/descriptors/SegmentAnnotateMerge.xml b/src/test/data/descriptors/SegmentAnnotateMerge.xml index 98ff94a..a64653e 100644 --- a/src/test/data/descriptors/SegmentAnnotateMerge.xml +++ b/src/test/data/descriptors/SegmentAnnotateMerge.xml @@ -77,12 +77,12 @@ - - CASOutputFreq - - 2 - - + + + + + + diff --git a/src/test/src/test_engine.cpp b/src/test/src/test_engine.cpp index e7f5b1c..e93a11f 100644 --- a/src/test/src/test_engine.cpp +++ b/src/test/src/test_engine.cpp @@ -510,7 +510,7 @@ void testCasMultiplier(uima::util::ConsoleUI & rclConsole) num++; CAS & seg = iter.next(); failIfNotTrue(seg.getDocumentText().length() > 0); - pEngine->getAnnotatorContext().releaseCAS(seg); + seg.release(); } failIfNotTrue(num==3); delete pEngine; @@ -519,9 +519,7 @@ void testCasMultiplier(uima::util::ConsoleUI & rclConsole) } -/* For now, aggregate engines do not handle CAS Multipliers correctly. - This test will fail if ran. - TODO: Implement CAS Multiplier for Aggregate +/* Test the ability to handle CAS Multipliers within an aggregate engine */ void testAggregateCASMultiplier(const util::ConsoleUI &rclConsole) { @@ -555,7 +553,7 @@ void testAggregateCASMultiplier(const util::ConsoleUI &rclConsole) // There should be one Dave in each segment failIfNotTrue(anIndex.getSize() == 1); - pEngine->getAnnotatorContext().releaseCAS(rcas); + rcas.release(); } failIfNotTrue(numSegments == 3); @@ -567,7 +565,9 @@ void testAggregateCASMultiplier(const util::ConsoleUI &rclConsole) /* - * This will also not work + * Test CAS Multiplier that combines input CASes. + * Note that the default action for input CASes is to drop if there is an output, which means + * if they will continue on with the flow. For now this default behavior cannot be overridden yet. */ void testAggregateCASCombiner(const util::ConsoleUI &rclConsole) { @@ -600,21 +600,29 @@ void testAggregateCASCombiner(const util::ConsoleUI &rclConsole) ++numOutputs; CAS &rcas = iter.next(); ANIndex tokenIdx = rcas.getAnnotationIndex(token); - // There should be three tokens in each segment, including the delimiter (.) - failIfNotTrue(tokenIdx.getSize() == 6); - - // CAS should have a single SourceDocumentInformation whose lastSegment is true - ANIterator srcDocIt = rcas.getAnnotationIndex(srcDocInfo).iterator(); - failIfNotTrue(srcDocIt.isValid()); - AnnotationFS info = srcDocIt.get(); - failIfNotTrue(info.getBooleanValue(lastSegment)); - srcDocIt.moveToNext(); - failIfNotTrue(srcDocIt.isValid()); - - pEngine->getAnnotatorContext().releaseCAS(rcas); + size_t numToken = tokenIdx.getSize(); + + // CAS should have a single SourceDocumentInformation. + // lastSegment should be false for intermediate CASes and true for the last CAS. + ANIterator srcDocIter = rcas.getAnnotationIndex(srcDocInfo).iterator(); + failIfNotTrue(srcDocIter.isValid()); + AnnotationFS info = srcDocIter.get(); + + // If we're at the final CAS + if (numOutputs == 4) { + failIfNotTrue(numToken == 12); + failIfNotTrue(info.getBooleanValue(lastSegment)); + } else { + failIfNotTrue(numToken == 3); + failIfNotTrue(!info.getBooleanValue(lastSegment)); + } + srcDocIter.moveToNext(); + failIfNotTrue(!srcDocIter.isValid()); + + rcas.release(); } - failIfNotTrue(numOutputs == 2); + failIfNotTrue(numOutputs == 4); delete cas; delete pEngine; @@ -622,6 +630,44 @@ void testAggregateCASCombiner(const util::ConsoleUI &rclConsole) } +/** Test for correctness in the Step type, which contains a tagged union */ +void testStep(const util::ConsoleUI &rclConsole) { + rclConsole.info("Test Step class starts"); + const icu::UnicodeString dummyName("This is a test string."); + + Step emptyStep; + Step stepWithName{internal::SimpleStep(dummyName)}; + Step stepWithFinal{internal::FinalStep(false)}; + + failIfNotTrue(stepWithName.getType() == Step::StepType::SIMPLESTEP); + failIfNotTrue(stepWithName.getSimpleStep()->getEngineName() == dummyName); + + failIfNotTrue(stepWithFinal.getType() == Step::StepType::FINALSTEP); + failIfNotTrue(stepWithFinal.getFinalStep()->getForceDropCAS() == false); + + + failIfNotTrue(emptyStep.getType() == Step::StepType::UNSPECIFIED); + // Getting concrete types on empty Step will return nullptr + failIfNotTrue((emptyStep.getFinalStep() || emptyStep.getSimpleStep() || emptyStep.getFinalStep()) == false); + + // Test assignment operator on empty step + emptyStep = stepWithName; + failIfNotTrue(emptyStep.getSimpleStep()->getEngineName() == dummyName); + + // Test copy constructor + Step copiedStep(stepWithName); + failIfNotTrue(copiedStep.getType() == Step::StepType::SIMPLESTEP); + failIfNotTrue(copiedStep.getSimpleStep()->getEngineName() == dummyName); + + // Test assignment operator on Step containing SimpleStep + copiedStep = stepWithFinal; + failIfNotTrue(copiedStep.getType() == Step::StepType::FINALSTEP); + failIfNotTrue(copiedStep.getFinalStep()->getForceDropCAS() == false); + + // ParallelStep is not supported yet. +} + + void mainTest(uima::util::ConsoleUI & rclConsole, const char * cpszCCSID, const TCHAR * cpszConfigFilename, @@ -636,10 +682,8 @@ void mainTest(uima::util::ConsoleUI & rclConsole, testCallingSequence3(rclConsole, cpszConfigFilename); } testCasMultiplier(rclConsole); -#if 0 testAggregateCASMultiplier(rclConsole); testAggregateCASCombiner(rclConsole); -#endif } int main(int argc, char * argv[]) /* @@ -674,7 +718,7 @@ int main(int argc, char * argv[]) /* /* before we init the res mgr, we test for the correct error */ testMissingResMgr(clConsole); - + testStep(clConsole); try { /* create a UIMA resource */ (void) uima::ResourceManager::createInstance(MAIN_TITLE); diff --git a/src/utils/runAECpp.cpp b/src/utils/runAECpp.cpp index a65c6ca..a205d8d 100644 --- a/src/utils/runAECpp.cpp +++ b/src/utils/runAECpp.cpp @@ -422,7 +422,7 @@ void process (AnalysisEngine * pEngine, CAS * cas, std::string in, std::string o } //release the CAS - pEngine->getAnnotatorContext().releaseCAS(outCas); + outCas.release(); cout << "runAECpp::processing new Cas " << i << endl; } @@ -441,8 +441,8 @@ void process (AnalysisEngine * pEngine, CAS * cas, std::string in, std::string o } //release CAS - pEngine->getAnnotatorContext().releaseCAS(outCas); - + // pEngine->getAnnotatorContext().releaseCAS(outCas); + outCas.release(); cout << "runAECpp::processing new Cas " << i << endl; }