blob: a2660138450bfeb4e5fc9f92f602d1c62defef53 [file] [log] [blame]
/*
* 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);
}
}