/* * 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. */ package org.apache.uima.examples.flow; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.uima.analysis_engine.AnalysisEngineProcessException; import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData; import org.apache.uima.cas.CAS; import org.apache.uima.flow.CasFlowController_ImplBase; import org.apache.uima.flow.CasFlow_ImplBase; import org.apache.uima.flow.FinalStep; import org.apache.uima.flow.Flow; import org.apache.uima.flow.FlowControllerContext; import org.apache.uima.flow.ParallelStep; import org.apache.uima.flow.SimpleStep; import org.apache.uima.flow.Step; import org.apache.uima.resource.ResourceInitializationException; /** * Simple FlowController that invokes components in a fixed sequence. */ public class AdvancedFixedFlowController extends CasFlowController_ImplBase { public static final String PARAM_ACTION_AFTER_CAS_MULTIPLIER = "ActionAfterCasMultiplier"; public static final String PARAM_ALLOW_CONTINUE_ON_FAILURE = "AllowContinueOnFailure"; public static final String PARAM_FLOW = "Flow"; private static final int ACTION_CONTINUE = 0; private static final int ACTION_STOP = 1; private static final int ACTION_DROP = 2; private static final int ACTION_DROP_IF_NEW_CAS_PRODUCED = 3; private ArrayList mSequence; private int mActionAfterCasMultiplier; private Set mAEsAllowingContinueOnFailure = new HashSet(); public void initialize(FlowControllerContext aContext) throws ResourceInitializationException { super.initialize(aContext); String[] flow = (String[])aContext.getConfigParameterValue(PARAM_FLOW); mSequence = new ArrayList(); for (int i = 0; i < flow.length; i++) { String[] aes = flow[i].split(","); if (aes.length == 1) { mSequence.add(new SimpleStep(aes[0])); } else { Collection keys = new ArrayList(); keys.addAll(Arrays.asList(aes)); mSequence.add(new ParallelStep(keys)); } } String actionAfterCasMultiplier = (String) aContext .getConfigParameterValue(PARAM_ACTION_AFTER_CAS_MULTIPLIER); if ("continue".equalsIgnoreCase(actionAfterCasMultiplier)) { mActionAfterCasMultiplier = ACTION_CONTINUE; } else if ("stop".equalsIgnoreCase(actionAfterCasMultiplier)) { mActionAfterCasMultiplier = ACTION_STOP; } else if ("drop".equalsIgnoreCase(actionAfterCasMultiplier)) { mActionAfterCasMultiplier = ACTION_DROP; } else if ("dropIfNewCasProduced".equalsIgnoreCase(actionAfterCasMultiplier)) { mActionAfterCasMultiplier = ACTION_DROP_IF_NEW_CAS_PRODUCED; } else if (actionAfterCasMultiplier == null) { mActionAfterCasMultiplier = ACTION_DROP_IF_NEW_CAS_PRODUCED; // default } else { throw new ResourceInitializationException(); // TODO } String[] aeKeysAllowingContinue = (String[])aContext .getConfigParameterValue(PARAM_ALLOW_CONTINUE_ON_FAILURE); if (aeKeysAllowingContinue != null) { mAEsAllowingContinueOnFailure.addAll(Arrays.asList(aeKeysAllowingContinue)); } } /* * (non-Javadoc) * * @see org.apache.uima.flow.CasFlowController_ImplBase#computeFlow(org.apache.uima.cas.CAS) */ public Flow computeFlow(CAS aCAS) throws AnalysisEngineProcessException { return new FixedFlowObject(0); } /* (non-Javadoc) * @see org.apache.uima.flow.FlowController_ImplBase#addAnalysisEngines(java.util.Collection) */ public synchronized void addAnalysisEngines(Collection aKeys) { // Append new keys as a ParallelStep at end of Sequence // This is just an example of what could be done. // Note that in general, a "Collection" is unordered mSequence.add(new ParallelStep(new ArrayList(aKeys))); } /* (non-Javadoc) * @see org.apache.uima.flow.FlowController_ImplBase#removeAnalysisEngines(java.util.Collection) */ public synchronized void removeAnalysisEngines(Collection aKeys) throws AnalysisEngineProcessException { // Remove keys from Sequence ... replace with null so step indices are still valid for (int i = 0; i < mSequence.size(); ++i) { Step step = (Step)mSequence.get(i); if (step instanceof SimpleStep && aKeys.contains(((SimpleStep)step).getAnalysisEngineKey())) { mSequence.set(i, null); } else if (step instanceof ParallelStep) { Collection keys = new ArrayList(((ParallelStep)step).getAnalysisEngineKeys()); keys.removeAll(aKeys); if (keys.isEmpty()) { mSequence.set(i, null); } else { mSequence.set(i, new ParallelStep(keys)); } } } } class FixedFlowObject extends CasFlow_ImplBase { private int currentStep; private boolean wasPassedToCasMultiplier = false; private boolean casMultiplierProducedNewCas = false; private boolean internallyCreatedCas = false; /** * Create a new fixed flow starting at step <code>startStep</code> of the fixed sequence. * * @param startStep * index of mSequence to start at */ public FixedFlowObject(int startStep) { this(startStep, false); } /** * Create a new fixed flow starting at step <code>startStep</code> of the fixed sequence. * * @param startStep * index of mSequence to start at * @param internallyCreatedCas * true to indicate that this Flow object is for a CAS that was produced by a * CasMultiplier within this aggregate. Such CASes area allowed to be dropped and not * output from the aggregate. * */ public FixedFlowObject(int startStep, boolean internallyCreatedCas) { currentStep = startStep; this.internallyCreatedCas = internallyCreatedCas; } /* * (non-Javadoc) * * @see org.apache.uima.flow.Flow#next() */ public Step next() throws AnalysisEngineProcessException { // if CAS was passed to a CAS multiplier on the last step, special processing // is needed according to the value of the ActionAfterCasMultiplier config parameter if (wasPassedToCasMultiplier) { switch (mActionAfterCasMultiplier) { case ACTION_STOP: return new FinalStep(); case ACTION_DROP: return new FinalStep(internallyCreatedCas); case ACTION_DROP_IF_NEW_CAS_PRODUCED: if (casMultiplierProducedNewCas) { return new FinalStep(internallyCreatedCas); } // else, continue with flow break; // if action is ACTION_CONTINUE, just continue with flow } wasPassedToCasMultiplier = false; casMultiplierProducedNewCas = false; } // Get next in sequence, skipping any disabled ones Step nextStep; synchronized (AdvancedFixedFlowController.this) { do { if (currentStep >= mSequence.size()) { return new FinalStep(); // this CAS has finished the sequence } nextStep = (Step) mSequence.get(currentStep++); } while (nextStep == null); } // if next step is a CasMultiplier, set wasPassedToCasMultiplier to true for next time if (stepContainsCasMultiplier(nextStep)) wasPassedToCasMultiplier = true; // now send the CAS to the next AE(s) in sequence. return nextStep; } /** * @param nextStep * @return */ private boolean stepContainsCasMultiplier(Step nextStep) { if (nextStep instanceof SimpleStep) { AnalysisEngineMetaData md = (AnalysisEngineMetaData) getContext() .getAnalysisEngineMetaDataMap().get(((SimpleStep)nextStep).getAnalysisEngineKey()); return md != null && md.getOperationalProperties() != null && md.getOperationalProperties().getOutputsNewCASes(); } else if (nextStep instanceof ParallelStep) { Iterator iter = ((ParallelStep)nextStep).getAnalysisEngineKeys().iterator(); while (iter.hasNext()) { String key = (String)iter.next(); AnalysisEngineMetaData md = (AnalysisEngineMetaData) getContext() .getAnalysisEngineMetaDataMap().get(key); if (md != null && md.getOperationalProperties() != null && md.getOperationalProperties().getOutputsNewCASes()) return true; } return false; } else return false; } /* * (non-Javadoc) * * @see org.apache.uima.flow.CasFlow_ImplBase#newCasProduced(CAS, String) */ public synchronized Flow newCasProduced(CAS newCas, String producedBy) throws AnalysisEngineProcessException { // record that the input CAS has been segmented (affects its subsequent flow) casMultiplierProducedNewCas = true; // start the new output CAS from the next node after the CasMultiplier that produced it int i = 0; while (!stepContains((Step)mSequence.get(i), producedBy)) i++; return new FixedFlowObject(i + 1, true); } /** * @param object * @param producedBy * @return */ private boolean stepContains(Step step, String producedBy) { if (step instanceof SimpleStep) { return ((SimpleStep)step).getAnalysisEngineKey().equals(producedBy); } else if (step instanceof ParallelStep) { Iterator iter = ((ParallelStep)step).getAnalysisEngineKeys().iterator(); while (iter.hasNext()) { String key = (String)iter.next(); if (key.equals(producedBy)) return true; } return false; } else return false; } /* (non-Javadoc) * @see org.apache.uima.flow.CasFlow_ImplBase#continueOnFailure(java.lang.String, java.lang.Exception) */ public boolean continueOnFailure(String failedAeKey, Exception failure) { return mAEsAllowingContinueOnFailure.contains(failedAeKey); } } }