blob: a1fb16a65c33166399dac8d6becdaec6d6cf319a [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.analysis_engine.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.uima.Constants;
import org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
import org.apache.uima.analysis_engine.metadata.CapabilityLanguageFlow;
import org.apache.uima.analysis_engine.metadata.FixedFlow;
import org.apache.uima.analysis_engine.metadata.FlowConstraints;
import org.apache.uima.analysis_engine.metadata.FlowControllerDeclaration;
import org.apache.uima.analysis_engine.metadata.SofaMapping;
import org.apache.uima.analysis_engine.metadata.impl.AnalysisEngineMetaData_impl;
import org.apache.uima.cas.CAS;
import org.apache.uima.resource.ResourceConfigurationException;
import org.apache.uima.resource.ResourceCreationSpecifier;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.impl.ResourceCreationSpecifier_impl;
import org.apache.uima.resource.metadata.Capability;
import org.apache.uima.resource.metadata.ConfigurationParameter;
import org.apache.uima.resource.metadata.ConfigurationParameterDeclarations;
import org.apache.uima.resource.metadata.Import;
import org.apache.uima.resource.metadata.MetaDataObject;
import org.apache.uima.resource.metadata.OperationalProperties;
import org.apache.uima.resource.metadata.impl.Import_impl;
import org.apache.uima.resource.metadata.impl.PropertyXmlInfo;
import org.apache.uima.resource.metadata.impl.XmlizationInfo;
import org.apache.uima.util.InvalidXMLException;
import org.apache.uima.util.Level;
import org.apache.uima.util.NameClassPair;
import org.apache.uima.util.XMLInputSource;
import org.apache.uima.util.XMLParser;
import org.apache.uima.util.XMLParser.ParsingOptions;
import org.w3c.dom.Element;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* Reference implementation of {@link AnalysisEngineDescription}. Note that this class contains two
* attributes of class Map, which are not supported by the default XML input/output routines.
* Therefore we override the {@link #writeAttributeAsElement(String,Class,Object,Writer)} and
* {@link #readAttributeFromXMLElement(String,Class,Element,XMLParser)} methods.
*
*
*/
public class AnalysisEngineDescription_impl extends ResourceCreationSpecifier_impl implements
AnalysisEngineDescription {
/**
* resource bundle for log messages
*/
private static final String LOG_RESOURCE_BUNDLE = "org.apache.uima.impl.log_messages";
/**
* Name of the "delegateAnalysisEngineSpecifiers" property. Change this if interface changes.
*/
protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
/**
* Name of the "delegateAnalysisEngineSpecifiersWithImports" property. Change this if interface
* changes.
*/
protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS = "delegateAnalysisEngineSpecifiersWithImports";
/**
* Name of the "delegateAnalysisEngineSpecifiers" XML Element. Change this if schema changes.
*/
protected String ELEM_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
private String mFrameworkImplementation;
private boolean mPrimitive;
private FlowControllerDeclaration mFlowControllerDeclaration;
private Map<String, ResourceSpecifier> mDelegateAnalysisEngineSpecifiers = new HashMap<String, ResourceSpecifier>();
private Map<String, MetaDataObject> mDelegateAnalysisEngineSpecifiersWithImports = new HashMap<String, MetaDataObject>();
private Map<String, Import> mProcessedImports = new HashMap<String, Import>();
private SofaMapping[] mSofaMappings;
static final long serialVersionUID = -8103625125291855592L;
/**
* Creates a new AnalysisEngineDescription_impl. Initializes the MetaData,
* DelegateAnalysisEngineSpecifiers, and ExternalResourcesRequired attributes.
*/
public AnalysisEngineDescription_impl() {
setMetaData(new AnalysisEngineMetaData_impl());
setFrameworkImplementation(Constants.JAVA_FRAMEWORK_NAME);
// Set default operational properties. These are used if the
// descriptor is constructed programatically, rather than parsed.
OperationalProperties opProps = UIMAFramework.getResourceSpecifierFactory()
.createOperationalProperties();
opProps.setModifiesCas(true);
opProps.setMultipleDeploymentAllowed(true);
opProps.setOutputsNewCASes(false);
getAnalysisEngineMetaData().setOperationalProperties(opProps);
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getFrameworkImplementation()
*/
public String getFrameworkImplementation() {
return mFrameworkImplementation;
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#setFrameworkImplementation(java.lang.String)
*/
public void setFrameworkImplementation(String aFrameworkImplementation) {
mFrameworkImplementation = aFrameworkImplementation;
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#isPrimitive()
*/
public boolean isPrimitive() {
return mPrimitive;
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#setPrimitive(boolean)
*/
public void setPrimitive(boolean aPrimitive) {
mPrimitive = aPrimitive;
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getAnnotatorImplementationName()
*/
public String getAnnotatorImplementationName() {
return getImplementationName();
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#setAnnotatorImplementationName(String)
*/
public void setAnnotatorImplementationName(String aImplementationName) {
setImplementationName(aImplementationName);
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getDelegateAnalysisEngineSpecifiers()
*/
public Map<String, ResourceSpecifier> getDelegateAnalysisEngineSpecifiers() throws InvalidXMLException {
resolveDelegateAnalysisEngineImports(UIMAFramework.newDefaultResourceManager(), false);
return Collections.unmodifiableMap(mDelegateAnalysisEngineSpecifiers);
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getDelegateAnalysisEngineSpecifiers()
*/
public Map<String, ResourceSpecifier> getDelegateAnalysisEngineSpecifiers(ResourceManager aResourceManager)
throws InvalidXMLException {
resolveDelegateAnalysisEngineImports(aResourceManager, false);
return Collections.unmodifiableMap(mDelegateAnalysisEngineSpecifiers);
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getDelegateAnalysisEngineSpecifiersWithImports()
*/
public Map<String, MetaDataObject> getDelegateAnalysisEngineSpecifiersWithImports() {
return mDelegateAnalysisEngineSpecifiersWithImports;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getFlowControllerDeclaration()
*/
public FlowControllerDeclaration getFlowControllerDeclaration() {
return mFlowControllerDeclaration;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#setFlowController(org.apache.uima.analysis_engine.metadata.FlowControllerDeclaration)
*/
public void setFlowControllerDeclaration(FlowControllerDeclaration aFlowControllerDeclaration) {
mFlowControllerDeclaration = aFlowControllerDeclaration;
}
public Map<String, ResourceSpecifier> getAllComponentSpecifiers(ResourceManager aResourceManager) throws InvalidXMLException {
if (aResourceManager == null) {
aResourceManager = UIMAFramework.newDefaultResourceManager();
}
resolveImports(aResourceManager);
Map<String, ResourceSpecifier> map = new HashMap<String, ResourceSpecifier>(mDelegateAnalysisEngineSpecifiers);
if (getFlowControllerDeclaration() != null) {
map.put(getFlowControllerDeclaration().getKey(), getFlowControllerDeclaration()
.getSpecifier());
}
return Collections.unmodifiableMap(map);
}
/**
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getAnalysisEngineMetaData()
*/
public AnalysisEngineMetaData getAnalysisEngineMetaData() {
return (AnalysisEngineMetaData) getMetaData();
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#getSofaMappings()
*/
public SofaMapping[] getSofaMappings() {
return mSofaMappings;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#setSofaMappings(org.apache.uima.analysis_engine.metadata.SofaMapping[])
*/
public void setSofaMappings(SofaMapping[] aSofaMappings) {
mSofaMappings = aSofaMappings;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#doFullValidation()
*/
public void doFullValidation() throws ResourceInitializationException {
// attempt to instantiate AE in "verification mode"
Map<String, Object> m = new HashMap<String, Object>();
m.put(AnalysisEngineImplBase.PARAM_VERIFICATION_MODE, Boolean.TRUE);
AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(this, m);
validateSofaMappings();
ae.newCAS();
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#doFullValidation(org.apache.uima.resource.ResourceManager)
*/
public void doFullValidation(ResourceManager aResourceManager)
throws ResourceInitializationException {
// attempt to instantiate AE in "verification mode"
Map<String, Object> m = new HashMap<String, Object>();
m.put(AnalysisEngineImplBase.PARAM_VERIFICATION_MODE, Boolean.TRUE);
AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(this, aResourceManager, m);
validateSofaMappings();
ae.newCAS();
}
/**
* Determines if the AnalysisEngineDescription is valid. An exception is thrown if it is not
* valid. This should be called from this Analysis Engine's initialize method. Note this does not
* check configuration parameter settings - that must be done by an explicit call to
* validateConfigurationParameterSettings.
*
* @param aResourceManager
* a ResourceManager instance to use to resolve imports by name.
*
* @throws ResourceInitializationException
* if <code>aDesc</code> is invalid
* @throws ResourceConfigurationException
* if the configuration parameter settings in <code>aDesc</code> are invalid
*/
public void validate(ResourceManager aResourceManager) throws ResourceInitializationException, ResourceConfigurationException {
// do validation common to all descriptor types (e.g. config parameters)
super.validate(aResourceManager);
// TypeSystem may not be specified for an Aggregate Analysis Engine
if (!isPrimitive() && getAnalysisEngineMetaData().getTypeSystem() != null) {
throw new ResourceInitializationException(
ResourceInitializationException.AGGREGATE_AE_TYPE_SYSTEM,
new Object[] { getSourceUrlString() });
}
//Keys in FixedFlow or LanguageCapabilityFlow must be defined
FlowConstraints fc = getAnalysisEngineMetaData().getFlowConstraints();
String[] keys = null;
if (fc instanceof FixedFlow) {
keys = ((FixedFlow)fc).getFixedFlow();
}
else if (fc instanceof CapabilityLanguageFlow) {
keys = ((CapabilityLanguageFlow)fc).getCapabilityLanguageFlow();
}
if (keys != null) {
for (int i = 0; i < keys.length; i++) {
if (!getDelegateAnalysisEngineSpecifiersWithImports().containsKey(keys[i])) {
throw new ResourceInitializationException(ResourceInitializationException.UNDEFINED_KEY_IN_FLOW,
new Object[]{getAnalysisEngineMetaData().getName(), keys[i], getSourceUrlString()});
}
}
}
}
/**
* Overrides{@link ResourceCreationSpecifier_impl#checkForInvalidParameterOverrides(ConfigurationParameter[], String)
* to validate parameter overrides in an aggregate AE. Also logs a warning for aggregate
* parameters with no declared overrides.
*
* @param aParams
* an array of ConfigurationParameters
* @param aGroupName
* name of groups in which these parameters are contained. Null if no group
* @param aResourceManager
* a ResourceManager instance to use to resolve imports by name.
*
* @throws ResourceInitializationException
* if there is an invalid parameter override declaration
*/
protected void checkForInvalidParameterOverrides(ConfigurationParameter[] aParams,
String aGroupName, ResourceManager aResourceManager) throws ResourceInitializationException {
//make sure delegate analysis engine specifiers are resolved using the correct resource manager
try {
resolveImports(aResourceManager);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
for (int i = 0; i < aParams.length; i++) {
String[] overrides = aParams[i].getOverrides();
if (overrides.length > 0 && isPrimitive()) {
throw new ResourceInitializationException(
ResourceInitializationException.PARAM_OVERRIDE_IN_PRIMITIVE, new Object[] {
aParams[i].getName(), getMetaData().getName(), getSourceUrlString() });
} else if (overrides.length == 0 && !isPrimitive()) {
UIMAFramework.getLogger(this.getClass()).logrb(Level.WARNING, this.getClass().getName(),
"checkForInvalidParameterOverrides", LOG_RESOURCE_BUNDLE,
"UIMA_aggregate_param_no_overrides__WARNING",
new Object[] { getMetaData().getName(), aParams[i].getName() });
}
for (int j = 0; j < overrides.length; j++) {
// overrides should be of form delegateKey/paramName
int slashPos = overrides[j].indexOf('/');
if (slashPos <= 0 || slashPos >= overrides[j].length()) {
throw new ResourceInitializationException(
ResourceInitializationException.INVALID_PARAM_OVERRIDE_SYNTAX, new Object[] {
overrides[j], aParams[i].getName(), getMetaData().getName(),
getSourceUrlString() });
}
String delegateKey = overrides[j].substring(0, slashPos);
String paramName = overrides[j].substring(slashPos + 1);
// get component descriptor (could be an AE or could be the FlowController)
ResourceSpecifier componentSpecifier = getComponentSpecifier(delegateKey);
if (componentSpecifier == null) {
throw new ResourceInitializationException(
ResourceInitializationException.INVALID_PARAM_OVERRIDE_NONEXISTENT_DELEGATE,
new Object[] { overrides[j], aParams[i].getName(), getMetaData().getName(),
delegateKey, getSourceUrlString() });
}
if (componentSpecifier instanceof ResourceCreationSpecifier) {
ConfigurationParameter overriddenParam = null;
ConfigurationParameterDeclarations delegateParamDecls = ((ResourceCreationSpecifier) componentSpecifier)
.getMetaData().getConfigurationParameterDeclarations();
if (aGroupName == null) // param not in group
{
overriddenParam = delegateParamDecls.getConfigurationParameter(null, paramName);
if (overriddenParam == null) {
throw new ResourceInitializationException(
ResourceInitializationException.INVALID_PARAM_OVERRIDE_NONEXISTENT_PARAMETER,
new Object[] { overrides[j], aParams[i].getName(), getMetaData().getName(),
delegateKey, paramName, getSourceUrlString() });
}
} else {
// make sure parameter exists in group
overriddenParam = delegateParamDecls.getConfigurationParameter(aGroupName, paramName);
if (overriddenParam == null) {
throw new ResourceInitializationException(
ResourceInitializationException.INVALID_PARAM_OVERRIDE_NONEXISTENT_PARAMETER_IN_GROUP,
new Object[] { overrides[j], aParams[i].getName(), getMetaData().getName(),
delegateKey, paramName, aGroupName, getSourceUrlString() });
}
}
}
}
}
}
/**
* Gets the ResourceSpecifier of one a component of this aggregate, based on its key. This may be
* the specifier of a component (i.e. delegate) AnalysisEngine, or it may be the specifier of the
* FlowController.
*
* @param key
* the key of the component specifier to get
* @return the specifier for the component, null if there is no component with the given key
* @throws ResourceInitializationException
* if there's a problem resolving imports
*/
public ResourceSpecifier getComponentSpecifier(String key) throws ResourceInitializationException {
ResourceSpecifier componentSpecifier;
if (getFlowControllerDeclaration() != null
&& key.equals(getFlowControllerDeclaration().getKey())) {
try {
getFlowControllerDeclaration().resolveImports();
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
componentSpecifier = getFlowControllerDeclaration().getSpecifier();
} else {
try {
componentSpecifier = (ResourceSpecifier) getDelegateAnalysisEngineSpecifiers().get(key);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
}
return componentSpecifier;
}
/**
* Validate SofA mappings and inputs/outputs for an aggregate AE.
*
* @param aDesc
*/
protected void validateSofaMappings() throws ResourceInitializationException {
if (this.isPrimitive())
return;
String aggName = this.getAnalysisEngineMetaData().getName();
// build an actual Map (key: componentKey@/@componentSofa) from the sofa mappings
// along the way check that all component keys and component sofa names exist
Map<String, String> sofamap = new TreeMap<String, String>();
SofaMapping[] sofaMappings = this.getSofaMappings();
if (sofaMappings != null) {
for (int s = 0; s < sofaMappings.length; s++) {
String componentKey = sofaMappings[s].getComponentKey();
ResourceSpecifier componentSpec = getComponentSpecifier(componentKey);
if (componentSpec == null) {
throw new ResourceInitializationException(
ResourceInitializationException.SOFA_MAPPING_HAS_UNDEFINED_COMPONENT_KEY,
new Object[] { componentKey, sofaMappings[s].getAggregateSofaName(), aggName,
getSourceUrlString() });
}
String componentSofaName = sofaMappings[s].getComponentSofaName();
if (componentSofaName == null) {
componentSofaName = CAS.NAME_DEFAULT_SOFA;
} else if (componentSpec instanceof AnalysisEngineDescription
&& !CAS.NAME_DEFAULT_SOFA.equals(componentSofaName)
&& !declaresSofa((AnalysisEngineDescription) componentSpec, componentSofaName)) {
throw new ResourceInitializationException(
ResourceInitializationException.SOFA_MAPPING_HAS_UNDEFINED_COMPONENT_SOFA,
new Object[] { componentKey, componentSofaName,
sofaMappings[s].getAggregateSofaName(), aggName, getSourceUrlString() });
}
String compoundKey = sofaMappings[s].getComponentKey() + "@/@"
+ sofaMappings[s].getComponentSofaName();
String aggSofaName = sofaMappings[s].getAggregateSofaName();
// check for double-mapping
String existingMapping = (String) sofamap.get(compoundKey);
if (existingMapping != null && !existingMapping.equals(aggSofaName)) {
throw new ResourceInitializationException(
ResourceInitializationException.SOFA_MAPPING_CONFLICT, new Object[] {
sofaMappings[s].getComponentSofaName(), sofaMappings[s].getComponentKey(),
aggName, existingMapping, aggSofaName, getSourceUrlString() });
} else {
sofamap.put(compoundKey, aggSofaName);
}
}
}
// Rules for SofAs:
// (1) Each component output sofa must be mapped to an aggregate output sofa
// (2) Each aggregate output sofa must be mapped to a component output sofa
// (3) Each component input sofa must be mapped to an aggregate input sofa OR a component output
// sofa
// (4) Each aggregate input sofa must be mapped to a component input sofa
// From (1) and (3) we derive that:
// Each component input sofa must be mapped to an aggregate input sofa OR an aggregate output
// sofa
// (which is easier to check)
// Exception: if aggregate contains a remote component, we cannot check that remote
// component's input or output sofas, so rules (2) and (4) cannot be checked.
boolean containsRemote = false;
Set<String> correctlyMappedAggregateOutputs = new HashSet<String>();
Set<String> correctlyMappedAggregateInputs = new HashSet<String>();
Iterator<Map.Entry<String, ResourceSpecifier>> iter;
try {
iter = getDelegateAnalysisEngineSpecifiers().entrySet().iterator();
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
while (iter.hasNext()) {
Map.Entry<String, ResourceSpecifier> entry = iter.next();
String componentKey = entry.getKey();
ResourceSpecifier delegateSpec = entry.getValue();
if (delegateSpec instanceof AnalysisEngineDescription) {
Capability[] caps = ((AnalysisEngineDescription) delegateSpec).getAnalysisEngineMetaData()
.getCapabilities();
for (int i = 0; i < caps.length; i++) {
// all component output sofas must be mapped to aggregate output sofas
String[] outputSofas = caps[i].getOutputSofas();
for (int j = 0; j < outputSofas.length; j++) {
String aggSofa = (String) sofamap.get(componentKey + "@/@" + outputSofas[j]);
if (aggSofa == null) // no declared mapping, name remains unchanged
{
aggSofa = outputSofas[j];
}
if (!capabilitiesContainSofa(aggSofa, true)) {
throw new ResourceInitializationException(
ResourceInitializationException.OUTPUT_SOFA_NOT_DECLARED_IN_AGGREGATE,
new Object[] { outputSofas[j], componentKey, aggName, getSourceUrlString() });
}
correctlyMappedAggregateOutputs.add(aggSofa);
}
// all component input sofas must be mapped to aggregate input OR output sofas
String[] inputSofas = caps[i].getInputSofas();
for (int j = 0; j < inputSofas.length; j++) {
String aggSofa = (String) sofamap.get(componentKey + "@/@" + inputSofas[j]);
if (aggSofa == null) // no declared mapping, name remains unchanged
{
aggSofa = inputSofas[j];
}
if (!capabilitiesContainSofa(aggSofa, false) && !capabilitiesContainSofa(aggSofa, true)) {
throw new ResourceInitializationException(
ResourceInitializationException.INPUT_SOFA_HAS_NO_SOURCE, new Object[] {
inputSofas[j], componentKey, aggName, getSourceUrlString() });
}
correctlyMappedAggregateInputs.add(aggSofa);
}
// also check default text sofa
String aggDefSofa = (String) sofamap.get(componentKey + "@/@" + CAS.NAME_DEFAULT_SOFA);
if (aggDefSofa != null) {
if (capabilitiesContainSofa(aggDefSofa, true)) {
correctlyMappedAggregateOutputs.add(aggDefSofa);
} else {
correctlyMappedAggregateInputs.add(aggDefSofa);
}
}
}
} // delegateSpec is not an AnalysisEngineDescription
else {
containsRemote = true;
}
}
if (!containsRemote) {
// check that all aggregate outputs and inputs were mapped correclty to
// component inputs/outputs
Capability[] caps = this.getAnalysisEngineMetaData().getCapabilities();
for (int i = 0; i < caps.length; i++) {
String[] sofas = caps[i].getOutputSofas();
for (int j = 0; j < sofas.length; j++) {
if (!correctlyMappedAggregateOutputs.contains(sofas[j])) {
throw new ResourceInitializationException(
ResourceInitializationException.AGGREGATE_SOFA_NOT_MAPPED, new Object[] {
sofas[j], aggName, getSourceUrlString() });
}
}
sofas = caps[i].getInputSofas();
for (int j = 0; j < sofas.length; j++) {
if (!correctlyMappedAggregateInputs.contains(sofas[j])) {
throw new ResourceInitializationException(
ResourceInitializationException.AGGREGATE_SOFA_NOT_MAPPED, new Object[] {
sofas[j], aggName, getSourceUrlString() });
}
}
}
}
}
private boolean declaresSofa(AnalysisEngineDescription aDesc, String aSofaName) {
Capability[] caps = aDesc.getAnalysisEngineMetaData().getCapabilities();
for (int i = 0; i < caps.length; i++) {
String[] sofas = caps[i].getOutputSofas();
for (int j = 0; j < sofas.length; j++) {
if (aSofaName.equals(sofas[j])) {
return true;
}
}
sofas = caps[i].getInputSofas();
for (int j = 0; j < sofas.length; j++) {
if (aSofaName.equals(sofas[j])) {
return true;
}
}
}
return false;
}
protected boolean capabilitiesContainSofa(String aSofaName, boolean aOutput) {
Capability[] caps = this.getAnalysisEngineMetaData().getCapabilities();
for (int i = 0; i < caps.length; i++) {
String[] sofas = aOutput ? caps[i].getOutputSofas() : caps[i].getInputSofas();
for (int j = 0; j < sofas.length; j++) {
if (aSofaName.equals(sofas[j])) {
return true;
}
}
}
return false;
}
/**
* Overridden to add Delegate AE Specifiers to the result list. Default introspection
* implementation won't return it because it has no set method. We've also overridden the XML
* import/export methods, though, so that set methods are not required.
*
* @see org.apache.uima.resource.MetaDataObject#listAttributes()
*/
public List<NameClassPair> listAttributes() {
List<NameClassPair> result = super.listAttributes();
result.add(new NameClassPair(PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS, Map.class
.getName()));
return result;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#toXML(java.io.OutputStream,
* boolean)
*/
public void toXML(OutputStream aOutputStream, boolean aPreserveDelegateAnalysisEngineImports)
throws SAXException, IOException {
if (aPreserveDelegateAnalysisEngineImports) {
// trick the writePropertyAsElement method into thinking that
// imports haven't been resolved yet
Map<String, ResourceSpecifier> tempMap = mDelegateAnalysisEngineSpecifiers;
mDelegateAnalysisEngineSpecifiers = Collections.emptyMap();
toXML(aOutputStream);
mDelegateAnalysisEngineSpecifiers = tempMap;
} else {
toXML(aOutputStream);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#toXML(java.io.Writer, boolean)
*/
public void toXML(Writer aWriter, boolean aPreserveDelegateAnalysisEngineImports)
throws SAXException, IOException {
if (aPreserveDelegateAnalysisEngineImports) {
// trick the writePropertyAsElement method into thinking that
// imports haven't been resolved yet
Map<String, ResourceSpecifier> tempMap = mDelegateAnalysisEngineSpecifiers;
mDelegateAnalysisEngineSpecifiers = Collections.emptyMap();
toXML(aWriter);
mDelegateAnalysisEngineSpecifiers = tempMap;
} else {
toXML(aWriter);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#toXML(org.xml.sax.ContentHandler,
* boolean, boolean)
*/
public void toXML(ContentHandler aContentHandler, boolean aWriteDefaultNamespaceAttribute,
boolean aPreserveDelegateAnalysisEngineImports) throws SAXException {
if (aPreserveDelegateAnalysisEngineImports) {
// trick the writePropertyAsElement method into thinking that
// imports haven't been resolved yet
Map<String, ResourceSpecifier> tempMap = mDelegateAnalysisEngineSpecifiers;
mDelegateAnalysisEngineSpecifiers = Collections.emptyMap();
toXML(aContentHandler, aWriteDefaultNamespaceAttribute);
mDelegateAnalysisEngineSpecifiers = tempMap;
} else {
toXML(aContentHandler, aWriteDefaultNamespaceAttribute);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#resolveImports(org.apache.uima.resource.ResourceManager)
*/
public void resolveImports(ResourceManager aResourceManager) throws InvalidXMLException {
resolveImports(new HashSet<String>(), aResourceManager);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngineDescription#resolveImports(java.util.Collection,
* org.apache.uima.resource.ResourceManager)
*/
public void resolveImports(Collection<String> aAlreadyImportedDelegateAeUrls,
ResourceManager aResourceManager) throws InvalidXMLException {
// add our own URL, if known, to the collection of already imported URLs
if (getSourceUrl() != null) {
aAlreadyImportedDelegateAeUrls.add(getSourceUrl().toString());
}
// resolve delegate AE imports (and recursively resolve imports therein)
resolveDelegateAnalysisEngineImports(aAlreadyImportedDelegateAeUrls, aResourceManager, true);
// resolve flow controller import
if (getFlowControllerDeclaration() != null) {
getFlowControllerDeclaration().resolveImports(aResourceManager);
}
// resolve imports in metadata (type systems, indexes, type priorities)
if (getAnalysisEngineMetaData() != null) {
getAnalysisEngineMetaData().resolveImports(aResourceManager);
}
// resolve imports in resource manager configuration
if (getResourceManagerConfiguration() != null) {
getResourceManagerConfiguration().resolveImports(aResourceManager);
}
}
/**
* Resolves imports of delegate Analysis Engines. This reads from the
* delegateAnalysisEngineSpecifiersWithImports map and populates the
* delegateAnalysisEngineSpecifiers map.
*
* @param aRecursive If true, this method will call {@link #resolveImports(Collection, ResourceManager)}
* on each delegate. If a cirular import is found, an exception will be thrown.
*/
protected void resolveDelegateAnalysisEngineImports(ResourceManager aResourceManager, boolean aRecursive)
throws InvalidXMLException {
// add our own URL, if known, to the collection of enclosing aggregate URLs
Set<String> urls = new HashSet<String>();
if (getSourceUrl() != null) {
urls.add(getSourceUrl().toString());
}
resolveDelegateAnalysisEngineImports(urls, aResourceManager, aRecursive);
}
/**
* Resolves imports of delegate Analysis Engines. This reads from the
* delegateAnalysisEngineSpecifiersWithImports map and populates the
* delegateAnalysisEngineSpecifiers map.
*
* @param aEnclosingAggregateAeUrls URLs of enclosing aggregate AEs. Used to detect circular imports.
* @param aRecursive If true, this method will call {@link #resolveImports(Collection, ResourceManager)}
* on each delegate. If a circular import is found, an exception will be thrown.
*/
protected void resolveDelegateAnalysisEngineImports(Collection<String> aEnclosingAggregateAeUrls,
ResourceManager aResourceManager, boolean aRecursive) throws InvalidXMLException {
Set<String> keys = new HashSet<String>(); // keep track of keys we've encountered
// so we can remove stale entries
for (Map.Entry<String, MetaDataObject> entry :
getDelegateAnalysisEngineSpecifiersWithImports().entrySet()) {
String key = entry.getKey();
keys.add(key);
if (entry.getValue() instanceof Import) {
Import aeImport = ((Import) entry.getValue());
// see if we processed this already
if (entry.getValue().equals(mProcessedImports.get(key))) {
continue;
}
// make sure Import's relative path base is set, to allow for
// users who create
// new import objects
if (aeImport instanceof Import_impl) {
((Import_impl) aeImport).setSourceUrlIfNull(this.getSourceUrl());
}
// locate import target
URL url = aeImport.findAbsoluteUrl(aResourceManager);
// check for resursive import
if (aEnclosingAggregateAeUrls.contains(url.toString())) {
String name = getMetaData() == null ? "<null>" : getMetaData().getName();
throw new InvalidXMLException(InvalidXMLException.CIRCULAR_AE_IMPORT, new Object[] {
name, url });
}
// parse import target
XMLInputSource input;
try {
input = new XMLInputSource(url);
} catch (IOException e) {
throw new InvalidXMLException(InvalidXMLException.IMPORT_FAILED_COULD_NOT_READ_FROM_URL,
new Object[] { url, aeImport.getSourceUrlString() }, e);
}
ResourceSpecifier spec = UIMAFramework.getXMLParser().parseResourceSpecifier(input);
// update entry in derived mDelegateAnalysisEngineSpecifiers map.
mDelegateAnalysisEngineSpecifiers.put(key, spec);
// add to processed imports map so we don't redo
mProcessedImports.put(key, aeImport);
// now resolve imports in ths delegate
if (spec instanceof AnalysisEngineDescription) {
Set<String> alreadyImportedUrls = new HashSet<String>(aEnclosingAggregateAeUrls);
alreadyImportedUrls.add(url.toString());
((AnalysisEngineDescription) spec).resolveImports(alreadyImportedUrls, aResourceManager);
}
} else {
// not an import -- copy directly to derived mDelegateAnalysisEngineSpecifiers map.
mDelegateAnalysisEngineSpecifiers.put(entry.getKey(), (ResourceSpecifier) entry.getValue());
// resolve imports recursively on the delegate
if (entry.getValue() instanceof AnalysisEngineDescription) {
((AnalysisEngineDescription) entry.getValue()).resolveImports(
aEnclosingAggregateAeUrls, aResourceManager);
}
}
}
// remove stale entries
List<String> staleKeys = new ArrayList<String>();
for (Map.Entry<String, ResourceSpecifier> entry : mDelegateAnalysisEngineSpecifiers.entrySet()) {
String key = entry.getKey();
if (!keys.contains(key)) {
staleKeys.add(key);
}
}
for (String key : staleKeys) {
mDelegateAnalysisEngineSpecifiers.remove(key);
mProcessedImports.remove(key);
}
}
/**
* Overridden to handle XML export of the DelegateAnalysisEngineSpecifiers attribute. This
* attribute has a value of type <code>Map</code>, which is not handled by the default XML
* export logic.
*
* @see org.apache.uima.MetaDataObject_impl#writePropertyAsElement(PropertyXmlInfo,String,ContentHandler)
*/
protected void writePropertyAsElement(PropertyXmlInfo aPropInfo, String aNamespace,
ContentHandler aContentHandler) throws SAXException {
if (PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS.equals(aPropInfo.propertyName)) {
// special logic here -- if imports have been resolved, then we want
// to write
// out the XML with those resolved imports. If imports have not been
// resolved,
// (mDelegateAnalysisEngineSpecifiers is empty), then we want to
// write out
// the original XML, which has the import declarations.
String propName = PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS;
if (mDelegateAnalysisEngineSpecifiers.isEmpty()) {
propName = PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS;
}
writeMapPropertyToXml(propName, ELEM_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS, "key",
"delegateAnalysisEngine", aPropInfo.omitIfNull, aNamespace, aContentHandler);
} else {
// for all other attributes, use the default superclass behavior
super.writePropertyAsElement(aPropInfo, aNamespace, aContentHandler);
}
}
/**
* Overridden to handle XML import of the DelegateAnalysisEngineSpecifiers attribute. This
* attribute has a value of type <code>Map</code>, which is not handled by the default XML
* import logic.
*
* @see org.apache.uima.resource.impl.MetaDataObject_impl#readPropertyValueFromXMLElement(org.apache.uima.resource.impl.PropertyXmlInfo,
* org.w3c.dom.Element, org.apache.uima.util.XMLParser)
*/
protected void readPropertyValueFromXMLElement(PropertyXmlInfo aPropXmlInfo, Element aElement,
XMLParser aParser, XMLParser.ParsingOptions aOptions) throws InvalidXMLException {
String propName = aPropXmlInfo.propertyName;
if (PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS.equals(propName)) {
readMapPropertyFromXml(propName, aElement, "key", "delegateAnalysisEngine", aParser,
aOptions, false);
} else {
// for all other attributes, use the default superclass behavior
super.readPropertyValueFromXMLElement(aPropXmlInfo, aElement, aParser, aOptions);
}
}
/**
* Overridden to set default operational properties if they are not specified in descriptor.
*/
public void buildFromXMLElement(Element aElement, XMLParser aParser, ParsingOptions aOptions)
throws InvalidXMLException {
super.buildFromXMLElement(aElement, aParser, aOptions);
if (getAnalysisEngineMetaData().getOperationalProperties() == null) {
OperationalProperties opProps = UIMAFramework.getResourceSpecifierFactory()
.createOperationalProperties();
opProps.setModifiesCas(true);
opProps.setMultipleDeploymentAllowed(true);
getAnalysisEngineMetaData().setOperationalProperties(opProps);
}
}
protected XmlizationInfo getXmlizationInfo() {
return XMLIZATION_INFO;
}
/**
* Static method to get XmlizationInfo, used by subclasses to set up their own XmlizationInfo.
*/
protected static XmlizationInfo getXmlizationInfoForClass() {
return XMLIZATION_INFO;
}
static final private XmlizationInfo XMLIZATION_INFO = new XmlizationInfo(
"analysisEngineDescription", new PropertyXmlInfo[] {
new PropertyXmlInfo("frameworkImplementation"),
new PropertyXmlInfo("primitive"),
new PropertyXmlInfo("annotatorImplementationName"),
new PropertyXmlInfo("delegateAnalysisEngineSpecifiersWithImports",
"delegateAnalysisEngineSpecifiers"), // NOTE: custom
// XMLization
new PropertyXmlInfo("flowControllerDeclaration", null),
new PropertyXmlInfo("metaData", null),
new PropertyXmlInfo("externalResourceDependencies"),
new PropertyXmlInfo("resourceManagerConfiguration", null),
new PropertyXmlInfo("sofaMappings") });
}