| /* |
| * 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.fit.factory; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.uima.analysis_engine.AnalysisEngine; |
| import org.apache.uima.analysis_engine.AnalysisEngineDescription; |
| import org.apache.uima.analysis_engine.metadata.SofaMapping; |
| import org.apache.uima.flow.FlowControllerDescription; |
| import org.apache.uima.resource.ResourceInitializationException; |
| import org.apache.uima.resource.metadata.TypePriorities; |
| import org.apache.uima.resource.metadata.TypeSystemDescription; |
| |
| /** |
| * This builder makes it easier to create an aggregate analysis engine. A typical use-case would |
| * involve initializing this builder with your preferred type system and type priorities (the latter |
| * may be null). This is followed by adding analysis engine descriptions one at a time until done. |
| * This makes it easy to have runtime decisions determine how the aggregate engine should be built. |
| * Finally, one of the create methods are called and an AnalysisEngine or AnalysisEngineDescription |
| * is returned. |
| * |
| * <p> |
| * This is an example taken from our test cases: |
| * </p> |
| * |
| * <pre><code> |
| * import static org.apache.uima.fit.factory.AnalysisEngineFactory.createPrimitiveDescription; |
| * |
| * AggregateBuilder builder = new AggregateBuilder(); |
| * builder.add(createPrimitiveDescription(Annotator1.class, typeSystemDescription), |
| * ViewNames.PARENTHESES_VIEW, "A"); |
| * builder.add(createPrimitiveDescription(Annotator2.class, typeSystemDescription), |
| * ViewNames.SORTED_VIEW, "B", |
| * ViewNames.SORTED_PARENTHESES_VIEW, "C", |
| * ViewNames.PARENTHESES_VIEW, "A"); |
| * builder.add(createPrimitiveDescription(Annotator3.class, typeSystemDescription), |
| * ViewNames.INITIAL_VIEW, "B"); |
| * AnalysisEngine aggregateEngine = builder.createAggregate(); |
| * </code></pre> |
| */ |
| public class AggregateBuilder { |
| |
| List<String> componentNames = new ArrayList<String>(); |
| |
| List<SofaMapping> sofaMappings = new ArrayList<SofaMapping>(); |
| |
| List<AnalysisEngineDescription> analysisEngineDescriptions = new ArrayList<AnalysisEngineDescription>(); |
| |
| TypeSystemDescription typeSystemDescription; |
| |
| TypePriorities typePriorities; |
| |
| FlowControllerDescription flowControllerDescription; |
| |
| /** |
| * The default no-args constructor calls |
| * {@link AggregateBuilder#AggregateBuilder(TypeSystemDescription, TypePriorities, FlowControllerDescription)} |
| * with null-valued args. |
| */ |
| public AggregateBuilder() { |
| this(null, null, null); |
| } |
| |
| /** |
| * Instantiate an AggregateBuilder with a given type system, type priorities, and flow controller. |
| * Generally, speaking it suffices to use the no arguments constructor |
| * |
| * @param typeSystemDescription |
| * this can be instantiated using {@link TypeSystemDescriptionFactory} |
| * @param typePriorities |
| * this can be instantiated using {@link TypePrioritiesFactory} |
| * @param flowControllerDescription |
| * this can be instantiated using {@link FlowControllerFactory} |
| */ |
| public AggregateBuilder(TypeSystemDescription typeSystemDescription, |
| TypePriorities typePriorities, FlowControllerDescription flowControllerDescription) { |
| this.typeSystemDescription = typeSystemDescription; |
| this.typePriorities = typePriorities; |
| this.flowControllerDescription = flowControllerDescription; |
| } |
| |
| /** |
| * This method simply calls {@link #add(String, AnalysisEngineDescription, String...)} using the |
| * result of {@link AnalysisEngineDescription#getAnnotatorImplementationName()} for the component |
| * name |
| * |
| * @param aed |
| * an analysis engine description to add to the aggregate analysis engine |
| * @param viewNames |
| * pairs of view names corresponding to a componentSofaName followed by the |
| * aggregateSofaName that it is mapped to. An even number of names must be passed in or |
| * else an IllegalArgumentException will be thrown. See |
| * {@link SofaMappingFactory#createSofaMapping(String, String, String)} |
| * |
| * @return the name of the component generated for the {@link AnalysisEngineDescription} |
| */ |
| public String add(AnalysisEngineDescription aed, String... viewNames) { |
| String componentName = aed.getAnalysisEngineMetaData().getName(); |
| if (componentName == null || componentName.equals("")) { |
| if (aed.isPrimitive()) { |
| componentName = aed.getAnnotatorImplementationName(); |
| } else { |
| componentName = "aggregate"; |
| } |
| } |
| if (componentNames.contains(componentName)) { |
| componentName = componentName + "." + (componentNames.size() + 1); |
| } |
| add(componentName, aed, viewNames); |
| return componentName; |
| } |
| |
| /** |
| * @param componentName |
| * the name of the component to add |
| * @param aed |
| * an analysis engine description to add to the aggregate analysis engine |
| * @param viewNames |
| * pairs of view names corresponding to a componentSofaName followed by the |
| * aggregateSofaName that it is mapped to. An even number of names must be passed in or |
| * else an IllegalArgumentException will be thrown. See |
| * {@link SofaMappingFactory#createSofaMapping(String, String, String)} |
| */ |
| public void add(String componentName, AnalysisEngineDescription aed, String... viewNames) { |
| if (componentNames.contains(componentName)) { |
| throw new IllegalArgumentException("the component name '" + componentName |
| + "' has already been used for another added analysis engine description."); |
| } |
| if (viewNames != null && viewNames.length % 2 != 0) { |
| throw new IllegalArgumentException("an even number of view names is required (as " |
| + "component view name, aggregate view name pairs) for the AggregateBuilder.add " |
| + "method. " + viewNames.length + " view names passed: " + Arrays.asList(viewNames)); |
| } |
| |
| analysisEngineDescriptions.add(aed); |
| componentNames.add(componentName); |
| |
| if (viewNames != null) { |
| for (int i = 0; i < viewNames.length; i += 2) { |
| sofaMappings.add(SofaMappingFactory.createSofaMapping(componentName, viewNames[i], |
| viewNames[i + 1])); |
| } |
| } |
| } |
| |
| /** |
| * Provide a sofa mapping for a component from the component's view to the aggregate view. |
| * |
| * @param componentName |
| * the name of the component |
| * @param componentViewName |
| * the name of the component view |
| * @param aggregateViewName |
| * the name of the aggregate view to map the component view to. |
| */ |
| public void addSofaMapping(String componentName, String componentViewName, |
| String aggregateViewName) { |
| if (componentNames.contains(componentName)) { |
| sofaMappings.add(SofaMappingFactory.createSofaMapping(componentName, componentViewName, |
| aggregateViewName)); |
| } else { |
| throw new IllegalArgumentException("No component with the name '" + componentName |
| + "' has been added to this builder. Sofa mappings may only be added for " |
| + "components that have been added to this builder. "); |
| } |
| } |
| |
| /** |
| * Set the flow controller description of the aggregate engine created by this builder. |
| * |
| * @param flowControllerDescription |
| * see {@link FlowControllerFactory} |
| */ |
| public void setFlowControllerDescription(FlowControllerDescription flowControllerDescription) { |
| this.flowControllerDescription = flowControllerDescription; |
| } |
| |
| /** |
| * This method simply delegates to |
| * {@link AnalysisEngineFactory#createEngine(List, TypeSystemDescription, TypePriorities, SofaMapping[], Object...)} |
| * with the data collected by this builder. |
| * |
| * @return an aggregate analysis engine |
| * @throws ResourceInitializationException |
| * if there is a problem during initialization |
| */ |
| public AnalysisEngine createAggregate() throws ResourceInitializationException { |
| return AnalysisEngineFactory.createEngine(analysisEngineDescriptions, componentNames, |
| typePriorities, sofaMappings.toArray(new SofaMapping[sofaMappings.size()]), |
| flowControllerDescription); |
| } |
| |
| /** |
| * This method simply delegates to |
| * {@link AnalysisEngineFactory#createEngineDescription(List, TypeSystemDescription, TypePriorities, SofaMapping[], Object...)} |
| * with the data collected by this builder. |
| * |
| * @return a description of an aggregate analysis engine |
| * @throws ResourceInitializationException |
| * if there is a problem during initialization |
| */ |
| public AnalysisEngineDescription createAggregateDescription() |
| throws ResourceInitializationException { |
| return AnalysisEngineFactory.createEngineDescription(analysisEngineDescriptions, |
| componentNames, typePriorities, |
| sofaMappings.toArray(new SofaMapping[sofaMappings.size()]), flowControllerDescription); |
| } |
| } |