You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2008/01/27 15:21:41 UTC

svn commit: r615608 [1/2] - in /incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima: analysis_engine/ analysis_engine/impl/ analysis_engine/impl/compatibility/ flow/impl/ uimacpp/

Author: schor
Date: Sun Jan 27 06:21:40 2008
New Revision: 615608

URL: http://svn.apache.org/viewvc?rev=615608&view=rev
Log:
[UIMA-721]  Still left to do, as an incremental improvement - replace use of CapabilityContainer with ResultSpecification, and do an appropriate union of input types needed by annotators in the Flow when computing the Capability Language Flow.

Modified:
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngine.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineImplBase.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/compatibility/AnnotatorAdapter.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowController.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowObject.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/uimacpp/CppUimajEngine.java

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngine.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngine.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngine.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngine.java Sun Jan 27 06:21:40 2008
@@ -24,6 +24,7 @@
 
 import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
 import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.TypeSystem;
 import org.apache.uima.collection.base_cpm.CasObjectProcessor;
 import org.apache.uima.jcas.JCas;
 import org.apache.uima.resource.ConfigurableResource;
@@ -510,13 +511,26 @@
   public void collectionProcessComplete() throws AnalysisEngineProcessException;
 
   /**
-   * A factory method used to create an instance of {@link ResultSpecification} for use with this
+   * <p>A factory method used to create an instance of {@link ResultSpecification} for use with this
    * AnalysisEngine. Applications use this method to construct <code>ResultSpecification</code>s
    * to pass to this AnalysisEngine's {@link #setResultSpecification(ResultSpecification)} method.
    * 
+   * <p>
+   * See also {@link #createResultSpecification(TypeSystem)} which should be used if the
+   * type system associated with this result specification is known at this point in time.
+   * 
    * @return a new instance of <code>ResultSpecification</code>
    */
   public ResultSpecification createResultSpecification();
+
+  /**
+   * A factory method used to create an instance of {@link ResultSpecification} for use with this
+   * AnalysisEngine. Applications use this method to construct <code>ResultSpecification</code>s
+   * to pass to this AnalysisEngine's {@link #setResultSpecification(ResultSpecification)} method.
+   * 
+   * @return a new instance of <code>ResultSpecification</code>
+   */
+  public ResultSpecification createResultSpecification(TypeSystem aTypeSystem);
 
   /**
    * Gets the {@link ResourceManager} used by this AnalysisEngine.

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java Sun Jan 27 06:21:40 2008
@@ -30,30 +30,82 @@
  * A <code>ResultSpecification</code> is a set of desired outputs from a Analysis Engine or
  * Annotator. Each output is a reference to either a {@link org.apache.uima.cas.Type} or a
  * {@link org.apache.uima.cas.Feature}.
+ * 
  * <p>
  * Annotator implementations may, but are not required to, check the <code>ResultSpecification</code> 
  * passed as a parameter to their {@link AnalysisComponent#setResultSpecification(ResultSpecification)}},
  * and produce only those <code>Type</code>s and <code>Feature</code>s that are part of the 
  * ResultSpecification.  Annotators can call the {@link #containsType(String)} and
  * {@link #containsFeature(String)} to determine which types and features belong to this
- * ResultSpecification and should be produced.
+ * ResultSpecification and should be produced.  
+ * 
+ * <p>
+ * The containsType(...) method also returns true for subtypes of types specified in the result specification.
+ * The containsFeature(...) method also returns true for all features of a type if the type's 
+ * allAnnotatorFeatures flag is set.  As a corner case, the containsFeature may return 
+ * true in this case even if the feature is not actually part of the type system.
+ * 
  * <p>
  * ResultSpecifications are language enabled to allow different values to be set and returned, based
- * on a ISO language identifier. There are two styles of the get and add methods: one takes an
+ * on a ISO language identifier. There are two styles of the get and add methods that add these 
+ * specifications to an instance of a ResultSpecification: one takes an
  * additional parameter specifying the language(s), the other doesn't have this parameter. Using the
  * one without the language parameter is equivalent to using the "x-unspecified" language. The
  * functions that add ResultSpecifications can do this for multiple languages at once because the
  * language parameter is an array of strings. The functions to retrieve a ResultSpecification
  * specify one particular language.
+ * 
+ * <p>The language x-unspecified is used like a wild-card, in that it matches any language.
+ * Result Specifications for particular types or features have an associated set of languages
+ * which they are set for.  That associated set of languages can include the "x-unspecified"
+ * language; if it does, then a query for that feature for any language will match.
+ * 
+ * <p>A result specification entry having a language set may contain languages with country codes, such as
+ * zh-cn for example.  A query for zh-cn would match, but a query for zh would not match, 
+ * if this entry only had the zh-cn language specified.
+ * 
+ * <p>But, a result specification entry having a language set containing just zh will match
+ * queries that specify zh-cn as the language, because a result-specification of zh is implied to
+ * mean the language zh regardless of the country.
+ * 
  * <p>
- * If you query the ResultSpecification with a language with a country code (e.g. en-US), results
- * for the base language (en) will be returned as well.
+ * Sometimes the methods to change the result specification replace the language(s), other times, the 
+ * language(s) are merged with any existing specification already present in the result specification 
+ * for the particular type or feature.
+ * <ul><li>Methods to "set" result types and/or features (or both) replace the current settings, for all languages.</li>
+ * <li>Methods to "add" 1 type or feature, with no language(s) specified 
+ * replace the current language setting for that type or feature with x-unspecified, 
+ * except for the following 2 methods</li>
+ * <li>Methods addResultType and addFeatureType, with languages specified, augment any existing
+ * languages that might already be specified for that type or feature.</li>
+ * </ul>
+ * 
+ * <p>Prior to any querying using the containsType or containsFeature methods, 
+ * the type system in use for this result specification may be specified by
+ * calling setTypeSystem(typeSystem). If it is available, the results of the containsType and
+ * containsFeature methods will be true for subtypes of the original types.
+ * The language specification in the ResultSpecification for a derived sub type 
+ * is computed as the union of all of the langauges specified for its supertypes.
+ * Likewise, the The allAnnotatorFeatures flag of a subtype is the logical union 
+ * of this property for all of its supertypes.
+ * 
+ * <p>The computation to enable this behavior for subtypes is called "compiling" the 
+ * result specification, and is done automatically, but only when 
+ * needed, and when there is an available type system
+ * specified for this result specification object, using 
+ * the setTypeSystem or compile methods.  The result of this computation is "cached".  If the result specification
+ * is subsequently updated in such a way as to make this computation invalid, the cache is 
+ * invalidated, and another compile operation will be transparently done, when and if needed. 
  * 
  */
 public interface ResultSpecification extends XMLizable, Serializable, Cloneable {
   /**
    * Retrieves the Types and Features that the AnalysisEngine or Annotator is requested to produce
-   * for the default language x-unspecified.
+   * for the default language x-unspecified (that is, regardless any language specification).
+   * <p>
+   * The set of types and features returned are just the ones that have been 
+   * explicitly set or added to the ResultSpecification, and doesn't include
+   * any derived subtypes, even if this ResultSpecification has been compiled. 
    * 
    * @return an array of {@link TypeOrFeature} objects that define the result types and features for
    *         the language x-unspecified.
@@ -62,8 +114,12 @@
 
   /**
    * Retrieves the Types and Features that the AnalysisEngine or Annotator is requested to produce
-   * for the specified language.
-   * 
+   * for the specified language. See the class comment for how languages are compared.
+   * <p>
+   * The set of types and features returned are just the ones that have been 
+   * explicitly set or added to the ResultSpecification, and doesn't include
+   * any derived subtypes, even if this ResultSpecification has been compiled. 
+    * 
    * @return an array of {@link TypeOrFeature} objects that define the result types and features for
    *         the specified language.
    */
@@ -71,7 +127,8 @@
 
   /**
    * Sets the Types and Features that the AnalysisEngine or Annotator is requested to produce for
-   * the language x-unspecified.
+   * the language x-unspecified, and removes all other type or feature information, that may be 
+   * previously present (e.g., for other languages)
    * 
    * @param aTypesAndFeatures
    *          an array of {@link TypeOrFeature} objects that define the result types and features
@@ -81,7 +138,8 @@
 
   /**
    * Sets the Types and Features that the AnalysisEngine or Annotator is requested to produce for
-   * the specified languages.
+   * the specified languages, and removes all other type or feature information, that may be 
+   * previously present (e.g., for other languages).  
    * 
    * @param aTypesAndFeatures
    *          an array of {@link TypeOrFeature} objects that define the result types and features
@@ -94,8 +152,9 @@
 
   /**
    * Adds a Result Type or Feature to this <code>ResultSpecification</code> for the language
-   * x-unspecified. If the current ToF is already contained in the result spec, the language
-   * specification for this ToF will be replaced with x-unspecified.
+   * x-unspecified. If there is already a same-named TypeOrFeature object contained in the 
+   * result spec, its language specification for this ToF will be replaced with x-unspecified,
+   * and its allAnnotatorFeatures flag will be replaced with the parameter's.  
    * 
    * @param aTypeOrFeature
    *          the Type or Feature to add for the language x-unspecified
@@ -104,8 +163,11 @@
 
   /**
    * Adds a Result Type or Feature to this <code>ResultSpecification</code> for the specified
-   * languages. If the current ToF is already contained in the result spec, the language
-   * specification for this ToF will be replaced with the specified languages.
+   * languages. If there is already a same-named TypeOrFeature object contained in the 
+   * result spec, the language
+   * specification for this ToF will be replaced with the specified languages,
+   * and its allAnnotatorFeatures flag will be replaced with the parameter's. If null is passed in for
+   * the aLanguageIDs, this is treated as if one language, x-unspecified, was given. 
    * 
    * @param aTypeOrFeature
    *          the Type or Feature to add for the specified languages
@@ -119,7 +181,8 @@
    * Adds an Type to this <code>ResultSpecification</code> for the language x-unspecified.
    * 
    * If the current Type is already contained in the result spec, the language specification for
-   * this Type will be replaced with x-unspecified.
+   * this Type will be replaced with x-unspecified,
+   * and its allAnnotatorFeatures flag will be replaced with the parameter's.
    * 
    * @param aTypeName
    *          the name of the Type to add for the language x-unspecified
@@ -131,8 +194,11 @@
   /**
    * Adds an Type to this <code>ResultSpecification</code> for the specified languages.
    * 
-   * If the given Type is already contained in the result spec, the languages specificied will be
+   * If the given Type is already contained in the result spec, the languages specified will be
    * added to those already associated with the Type in this ResultSpec.
+   * The given type's allAnnotatorFeatures is logically "or"ed with any existing value. 
+   * If null is passed in for
+   * the aLanguageIDs, this is treated as if one language, x-unspecified, was given.
    * 
    * @param aTypeName
    *          the name of the Type to add for the specified languages
@@ -146,8 +212,8 @@
   /**
    * Adds a Feature to this <code>ResultSpecification</code> for the language x-unspecified.
    * 
-   * If the given Type is already contained in the result spec, the languages specificied will be
-   * added to those already associated with the Type in this ResultSpec.
+   * If the given Type is already contained in the result spec, the languages associated with that type will be
+   * replaced by x-unspecified.
    * 
    * @param aFullFeatureName
    *          the fully-qualified name of the Feature to add for the language x-unspecified
@@ -158,7 +224,9 @@
    * Adds a Feature to this <code>ResultSpecification</code> for the specified languages.
    * 
    * If the current Feature is already contained in the result spec, the language specification for
-   * this Feature will be replaced with the specified languages.
+   * this Feature will be augmented (added to) with the specified languages.
+   * If null is passed in for
+   * the aLanguageIDs, this is treated as if one language, x-unspecified, was given.
    * 
    * @param aFullFeatureName
    *          the fully-qualified name of the Feature to add for the specified languages
@@ -169,23 +237,27 @@
   public void addResultFeature(String aFullFeatureName, String[] aLanguageIDs);
 
   /**
-   * Compiles this <code>ResultSpecification</code> using a specific {@link TypeSystem}. This
-   * allows the {@link #containsType(String)} and {@link #containsFeature(String)} methods to
-   * properly consider the inheritance of types and to expand features when this
-   * <code>ResultSpecification</code> contains Types with <code>allAnnotatorFeatures</code> set
-   * to true.
-   * <p>
-   * This method is called automatically by the Analysis Engine. Annotators and Applications do not
-   * need to call it.
-   * 
+  * Compiles this <code>ResultSpecification</code> using a specific {@link TypeSystem}. The
+  * result is cached and used by the {@link #containsType(String)} and {@link #containsFeature(String)} methods to
+  * properly consider the inheritance of types and to restrict allAnnotatorFeatures 
+  * to just those features defined in the type system when this
+  * <code>ResultSpecification</code> contains Types with <code>allAnnotatorFeatures</code> set
+  * to true.
+  * <p>
+  * This method is called automatically internally when needed. Framework code, Annotators and Applications do not
+  * need to call it.  
+  * 
    * @param aTypeSystem
    *          the Type System used to determine which features belong to each Type
+   * @deprecated as of 2.3.0  Now called automatically internally when needed
    */
   public void compile(TypeSystem aTypeSystem);
 
   /**
    * Determines whether this <code>ResultSpecification</code> contains the specified Type for the
-   * language x-unspecified.
+   * language x-unspecified.  If a type system is available to the result specification, 
+   * a type will be considered to be contained in the result spec, also, if it
+   * is a subtype of the types originally specified to be in the result specification.
    * 
    * @param aTypeName
    *          the name of the type
@@ -197,12 +269,15 @@
 
   /**
    * Determines whether this <code>ResultSpecification</code> contains the specified Type for the
-   * specified language
+   * specified language.  A type is considered to be contained in the result spec, also, if it
+   * is a subtype of the types originally specified to be in the result specification for this language.
    * 
    * @param aTypeName
    *          the name of the type
    * @param aLanguage
-   *          the language to search for
+   *          the language to search for.  A null value or the value x-unspecified for this argument disables
+   *          the language test - any language will match. The language is matched after excluding any
+   *          country code, if present.
    * 
    * @return true if and only if this <code>ResultSpecification</code> contains the type with name
    *         <code>aTypeName</code> for the specified language.
@@ -211,7 +286,11 @@
 
   /**
    * Determines whether this <code>ResultSpecification</code> contains the specified Feature for
-   * the language x-unspecified.
+   * the language x-unspecified.  Feature names are fully qualified, consisting of the 
+   * type name plus the feature-of-that-type name.  A feature ttt:fff is contained in the result spec if
+   * that fff is specified for type ttt or any supertype of ttt in the result spec.
+   * A feature can be specified in the result spec explicitly or by specifying a type or supertype
+   * of the feature's type having the allAnnotatorFeatures flag set.
    * 
    * @param aFullFeatureName
    *          the fully-qualified name of the feature, in the form MyTypeName:MyFeatureName.
@@ -223,12 +302,18 @@
 
   /**
    * Determines whether this <code>ResultSpecification</code> contains the specified Feature for
-   * the specified language.
+   * the specified language. Feature names are fully qualified, consisting of the 
+   * type name plus the feature-of-that-type name.  A feature ttt:fff is contained in the result spec if
+   * that fff is specified for type ttt or any supertype of ttt in the result spec.
+   * A feature can be specified in the result spec explicitly or by specifying a type or supertype
+   * of the feature's type having the allAnnotatorFeatures flag set.
    * 
    * @param aFullFeatureName
    *          the fully-qualified name of the feature, in the form MyTypeName:MyFeatureName.
    * @param aLanguage
-   *          the language to search for
+   *          the language to search for.  A null value or the value x-unspecified for this argument disables
+   *          the language test - any language will match.  A value with a country code
+   *          in addition to a language will match a result-spec without the country code.
    * 
    * @return true if and only if this <code>ResultSpecification</code> contains the feature with
    *         name <code>aFullFeatureName</code> for the specified language.
@@ -237,7 +322,12 @@
 
   /**
    * Adds the output types and features from the specified capabilities to this
-   * <code>ResultSpecification</code>.
+   * <code>ResultSpecification</code>.  
+   * <p>
+   * If a Type being added is already contained in the ResultSpecification, the languages 
+   * from the Capabilities entry for this type will be
+   * added to those already associated with the Type in this ResultSpec.
+   * The given capability instance's allAnnotatorFeatures is logically "or"ed with any existing value. 
    * 
    * @param aCapabilities
    *          capabilities to add
@@ -246,18 +336,23 @@
 
   /**
    * Adds either outputs or inputs from the specified capabilities to this
-   * <code>ResultSpecification</code>.
-   * 
+   * <code>ResultSpecification</code>.  
+   * <p>
+   * If a Type being added is already contained in the ResultSpecification, the languages 
+   * from the Capabilities entry for this type will be
+   * added to those already associated with the Type in this ResultSpec.
+   * The given capability instance's allAnnotatorFeatures is logically "or"ed with any existing value. 
+
    * @param aCapabilities
    *          capabilities to add
    * @param aOutputs
    *          true to add the output types/features to this ResultSpecification, false to add the
-   *          input types/features to this ResultSpecification.
+   *          input types/features to this ResultSpecification.  
    */
   public void addCapabilities(Capability[] aCapabilities, boolean aOutputs);
 
   /**
-   * removes the specified TypeOrFeature from this <code>ResultSpecification</code>.
+   * removes the specified TypeOrFeature from this <code>ResultSpecification</code>.  
    * 
    * @param aTypeOrFeature
    *          the Type or Feature to remove
@@ -265,10 +360,22 @@
   public void removeTypeOrFeature(TypeOrFeature aTypeOrFeature);
 
   /**
-   * create a copy of the current object
+   * create a copy of the current object.
    * 
    * @return Object returns a copy of the current object
    */
   public Object clone();
+
+  /**
+   * set the type system associated with this result specification.  It is used
+   * to augment any types with their subtypes
+   * @param ts the CAS Type System
+   */
+  public void setTypeSystem(TypeSystem ts);
+
+  /**
+   * get the type system associated with this result specification.
+   */
+  public TypeSystem getTypeSystem();
 
 }

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineImplBase.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineImplBase.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineImplBase.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineImplBase.java Sun Jan 27 06:21:40 2008
@@ -26,6 +26,7 @@
 
 import org.apache.uima.UIMAFramework;
 import org.apache.uima.UimaContextAdmin;
+import org.apache.uima.analysis_engine.AnalysisEngine;
 import org.apache.uima.analysis_engine.AnalysisEngineManagement;
 import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.analysis_engine.CasIterator;
@@ -294,6 +295,13 @@
    */
   public ResultSpecification createResultSpecification() {
     return new ResultSpecification_impl();
+  }
+
+  /**
+   * @see AnalysisEngine#createResultSpecification(TypeSystem)
+   */
+  public ResultSpecification createResultSpecification(TypeSystem aTypeSystem) {
+    return new ResultSpecification_impl(aTypeSystem);
   }
 
   /**

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java Sun Jan 27 06:21:40 2008
@@ -19,7 +19,9 @@
 
 package org.apache.uima.analysis_engine.impl;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.uima.Constants;
@@ -62,6 +64,10 @@
    * current class
    */
   private static final Class CLASS_NAME = PrimitiveAnalysisEngine_impl.class;
+ 
+  private static final String [] X_UNSPECIFIED = new String [] {"x-unspecified"};
+ 
+  private static final String [] EMPTY_STRING_ARRAY = new String [0];
 
   private ResultSpecification mCurrentResultSpecification;
 
@@ -349,18 +355,16 @@
         AbstractCas casToPass = getCasManager().getCasInterface(view, requiredInterface);
 
         // check if there was a change in the ResultSpecification or in
-        // the TypeSystem. If so, recompile the ResultSpecification and
+        // the TypeSystem. If so, set the changed type system into the ResultSpecification and
         // inform the component
         if (mResultSpecChanged || mLastTypeSystem != view.getTypeSystem()) {
           mLastTypeSystem = view.getTypeSystem();
-          mCurrentResultSpecification.compile(mLastTypeSystem);
+          mCurrentResultSpecification.setTypeSystem(mLastTypeSystem);
           // the actual ResultSpec we send to the component is formed by
           // looking at this primitive AE's declared output types and eliminiating
           // any that are not in mCurrentResultSpecification.
           ResultSpecification analysisComponentResultSpec = computeAnalysisComponentResultSpec(
                   mCurrentResultSpecification, getAnalysisEngineMetaData().getCapabilities());
-          // compile result spec - necessary to get type subsumption to work properly
-          analysisComponentResultSpec.compile(mLastTypeSystem);
           mAnalysisComponent.setResultSpecification(analysisComponentResultSpec);
           mResultSpecChanged = false;
         }
@@ -415,29 +419,60 @@
    */
   protected ResultSpecification computeAnalysisComponentResultSpec(
           ResultSpecification inputResultSpec, Capability[] capabilities) {
-    ResultSpecification newResultSpec = new ResultSpecification_impl();
-    for (int i = 0; i < capabilities.length; i++) {
-      Capability cap = capabilities[i];
-      TypeOrFeature[] outputs = cap.getOutputs();
-      String[] languages = cap.getLanguagesSupported();
-      if (languages.length == 0) {
-        languages = new String[] { "x-unspecified" };
+    ResultSpecification newResultSpec = new ResultSpecification_impl(inputResultSpec.getTypeSystem());
+    List<String> languagesToAdd = new ArrayList<String>();
+ 
+    for (Capability capability : capabilities) {
+      TypeOrFeature[] outputs = capability.getOutputs();
+      String[] languages = capability.getLanguagesSupported();
+      if (null == languages || languages.length == 0) {
+        languages = X_UNSPECIFIED;
       }
-      for (int j = 0; j < outputs.length; j++) {
-        for (int k = 0; k < languages.length; k++) {
-          if (outputs[j].isType()
-                  && inputResultSpec.containsType(outputs[j].getName(), languages[k])) {
-            newResultSpec.addResultType(outputs[j].getName(), outputs[j].isAllAnnotatorFeatures(),
-                    new String[] { languages[k] });
-          } else if (!outputs[j].isType()
-                  && inputResultSpec.containsFeature(outputs[j].getName(), languages[k])) {
-            newResultSpec.addResultFeature(outputs[j].getName(), new String[] { languages[k] });
+      
+      for (TypeOrFeature tof : outputs) {
+        String tofName = tof.getName();
+        languagesToAdd.clear();
+        for (String language : languages) {
+          if ((tof.isType() && inputResultSpec.containsType(tofName, language)) ||
+              (!tof.isType() && inputResultSpec.containsFeature(tofName, language))) {
+            languagesToAdd.add(language);
           }
         }
+        if (0 < languagesToAdd.size()) {
+          if (tof.isType()) {
+            newResultSpec.addResultType(tofName, tof.isAllAnnotatorFeatures(), 
+                languagesToAdd.toArray(EMPTY_STRING_ARRAY));
+          } else {
+            newResultSpec.addResultFeature(tofName, languagesToAdd.toArray(EMPTY_STRING_ARRAY));
+          }  
+        }
       }
     }
-    return newResultSpec;
+    return newResultSpec;    
   }
+    
+//    for (int i = 0; i < capabilities.length; i++) {
+//      Capability cap = capabilities[i];
+//      TypeOrFeature[] outputs = cap.getOutputs();
+//      String[] languages = cap.getLanguagesSupported();
+//      if (languages.length == 0) {
+//        languages = X_UNSPECIFIED;
+//      }
+//      for (int j = 0; j < outputs.length; j++) {
+//        for (int k = 0; k < languages.length; k++) {
+//          if (outputs[j].isType()
+//                  && inputResultSpec.containsType(outputs[j].getName(), languages[k])) {
+//            newResultSpec.addResultType(outputs[j].getName(), outputs[j].isAllAnnotatorFeatures(),
+//                    new String[] { languages[k] });
+//          } else if (!outputs[j].isType()
+//                  && inputResultSpec.containsFeature(outputs[j].getName(), languages[k])) {
+//            newResultSpec.addResultFeature(outputs[j].getName(), new String[] { languages[k] });
+//          }
+//        }
+//      }
+//    }
+//    return newResultSpec;
+//  }
 
   /**
    * Calls the Analysis Component's next() method.

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java Sun Jan 27 06:21:40 2008
@@ -19,19 +19,17 @@
 
 package org.apache.uima.analysis_engine.impl;
 
+import java.util.ArrayList;
+import java.util.BitSet;
 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.Vector;
 
 import org.apache.uima.analysis_engine.ResultSpecification;
 import org.apache.uima.analysis_engine.TypeOrFeature;
-import org.apache.uima.cas.Feature;
 import org.apache.uima.cas.Type;
 import org.apache.uima.cas.TypeSystem;
+import org.apache.uima.cas.text.Language;
 import org.apache.uima.resource.metadata.Capability;
 import org.apache.uima.resource.metadata.impl.MetaDataObject_impl;
 import org.apache.uima.resource.metadata.impl.PropertyXmlInfo;
@@ -40,137 +38,427 @@
 /**
  * Reference implementaion of {@link ResultSpecification}.
  * 
+ * Notes on the implementation
  * 
+ * There are two ways this data is used:  with and without "compiling"
+ *   Compiling means: adding subtypes of types and adding all features of a type
+ *   Uncompiled form is called ORIGINAL.
+ *   
+ *   Compiling is deferred - until the first reference to containsType or Feature.
+ *   
+ * Many instances of this class are made, sometimes via cloning.
+ * 
+ * Sometimes types and features are deleted - the intent is to do this operation on the
+ * uncompiled form, and then "recompile" it.
+ * 
+ * Types and Features are kept on a per-language basis.  Language can include a special value,
+ * x-unspecified, which "matches" any other language.
+ * 
+ * Language specifications are simplified to eliminate the country part.  All refs to 
+ * test if a type or feature is in the result spec for a language uses the simplified language.
+ * 
+ * Set operations are done to combine, for a particular type or feature, the languages for which it is valid.
+ * This is a Union operation
+ * Set operations are done to union the input types/features with the output types/features when computing the default
+ * result-spec for an aggregate.
+ * Set operations are done to intersect the result spec with the output capabilities of a component.
+ * 
+ * Languages are represented as integers; there is a hash table from the string to the integer, and
+ * an array to go from integer to lang string.
+ * 
+ * A result set of ORIGINALs consists of types/features with associated language sets. 
  */
 public final class ResultSpecification_impl extends MetaDataObject_impl implements
         ResultSpecification {
 
+//  private enum AddMode {ORIGINAL, COMPILED};
+//  private AddMode addMode = AddMode.ORIGINAL;
+//  
   private static final long serialVersionUID = 8516517600467270594L;
 
-  static final String UNSPECIFIED_LANGUAGE = "x-unspecified";
+  static final int UNSPECIFIED_LANGUAGE_INDEX = 0;
 
   /**
    * main language separator e.g 'en' and 'en-US'
    */
   private static final char LANGUAGE_SEPARATOR = '-';
-
+  
   /**
-   * Map from TypeOrFeature objects to HashSets that include the language codes (Strings) for which
-   * that type or feature should be produced.
+   * 
+   */
+  private boolean needsCompilation = true;
+  
+  private final Map<String, Integer> lang2int; 
+  
+  private int getLanguageWithoutCountryIndex(String language) {
+    return getLanguageIndex(getLanguageWithoutCountry(language));
+  }
+  
+  private int getLanguageIndex(String language) {
+    Integer r = lang2int.get(language);
+    if (null == r) {
+      int i = lang2int.size();
+      lang2int.put(language, Integer.valueOf(i));
+      return i;
+    }
+    return r.intValue();
+  }
+
+  private class ToF_Languages {
+    public TypeOrFeature tof;
+    public BitSet languages;
+    
+    ToF_Languages(TypeOrFeature aTof, String[] aLanguages) {
+      tof = aTof;
+      languages = new BitSet();
+      for (String lang : aLanguages) {
+        languages.set(getLanguageIndex(lang));
+      }
+    }
+    
+    ToF_Languages(TypeOrFeature aTof, BitSet aLanguages) {
+      tof = aTof;
+      languages = aLanguages;
+    }
+    
+    public Object clone() {
+      return new ToF_Languages((TypeOrFeature) tof.clone(), (BitSet)languages.clone());
+    }
+  }
+  
+  /**
+   * hash map used to map fully qualified type and feature names to associated
+   * ToF_Languages instances.  This used for ORIGINAL types and features.
+   * 
+   * Another hash map is used for compiled types and features - these include
+   * the subtypes of the ORIGINAL types.  We keep the originals because the
+   * operations of adding and removing types and features are done with respect
+   * to the originals, only, and then the other map for compiled types is recomputed.
+   * 
+   * A case in particular: we need to be able to distinguish which types were
+   * originally marked allAnnotatorFeatures, versus those types which were
+   * added because they were subtypes.  The corner case happens when a type is both
+   * an original and is also an added-via-subtype, where the allAnnotatorFeatures
+   * flag of the original is not set but the subtype version is set.
+   * 
    */
-  private Map mTypesAndFeatures = new HashMap();
+  private final Map<String, ToF_Languages> name2tof_langs;
 
   /**
-   * Map from String type or feature names to HashSets that include the language codes (Strings) for
-   * which that type or feature should be produced. This is populated by the compile() method, and
-   * includes subtypes as well as the individual feature names for types that have
-   * allAnnotatorFeatures=true.
+   * hash map used to map fully qualified type and feature names to associated
+   * ToF_Languages instances.  This used for COMPILED types and features.
    */
-  private Map mCompiledNameToLanguageMap = new HashMap();
 
+  private final Map<String, ToF_Languages> withSubtypesName2tof_langs;
+  
+//  /**
+//   * Map from TypeOrFeature objects to HashSets that include the language codes (Strings) for which
+//   * that type or feature should be produced.
+//   */
+//  private Map<TypeOrFeature, Set<String>> mTypesAndFeatures = new HashMap<TypeOrFeature, Set<String>>();
+//
+//  /**
+//   * Map from String type or feature names to HashSets that include the language codes (Strings) for
+//   * which that type or feature should be produced. This is populated by the compile() method, and
+//   * includes subtypes as well as the individual feature names for types that have
+//   * allAnnotatorFeatures=true.
+//   */
+//  private final Map<String, Set<String>> mCompiledNameToLanguageMap = 
+//                                   new HashMap<String, Set<String>>();
+  
   /**
    * Default language set to use if nothing else is specified
    */
-  private HashSet mDefaultLanguage = new HashSet();
+  private static final String[] mDefaultLanguage = new String[] {Language.UNSPECIFIED_LANGUAGE};
 
   /**
-   * constructor init the default languge set with the language x-unspecified
+   * The type system used to compute the subtypes and allAnnotatorFeatures of types
+   */
+  private TypeSystem mTypeSystem;
+
+  /**
+   * constructor:  init the default languge set with the language x-unspecified
    */
   public ResultSpecification_impl() {
-    mDefaultLanguage.add(UNSPECIFIED_LANGUAGE);
+    name2tof_langs = new HashMap<String, ToF_Languages>();
+    withSubtypesName2tof_langs = new HashMap<String, ToF_Languages>();
+    lang2int = new HashMap<String, Integer>();
+    lang2int.put(Language.UNSPECIFIED_LANGUAGE, 0); 
   }
 
   /**
+   * Constructor specifying the type system
+   *   this should always be used in preference to the 0 argument version
+   *   if the type system is available.  Otherwise, the type system *must*
+   *   be set via a method call prior to querying the result spec, with the
+   *   one exception of the method getResultTypesAndFeaturesWithoutCompiling
+   * @param aTypeSystem
+   */
+  public ResultSpecification_impl(TypeSystem aTypeSystem) {
+    this();
+    mTypeSystem = aTypeSystem;
+  }
+  
+  private ResultSpecification_impl(ResultSpecification_impl original) {
+    name2tof_langs = new HashMap<String, ToF_Languages>(original.name2tof_langs.size());
+    withSubtypesName2tof_langs = new HashMap<String, ToF_Languages>(original.withSubtypesName2tof_langs.size());
+    lang2int = original.lang2int;  // shared hash map - to save space.
+    for (Map.Entry<String, ToF_Languages> entry : original.name2tof_langs.entrySet()) {
+      ToF_Languages tof_langs = entry.getValue();
+      
+      // note: TypeOrFeature instances are not cloned, but shared
+      //   If they are modified, things may break
+      name2tof_langs.put(entry.getKey(), 
+          new ToF_Languages(tof_langs.tof, (BitSet)(tof_langs.languages.clone())));
+    }
+    mTypeSystem = original.mTypeSystem;
+  }
+
+  private void compileIfNeeded() {
+    if (needsCompilation) {
+      compile();
+    }
+  }
+  
+  private String getLanguageWithoutCountry(String language) {
+    String baseLanguage = language;
+    int index = language.indexOf(LANGUAGE_SEPARATOR);
+    if (index > -1) {
+      baseLanguage = language.substring(0, index);
+    }
+    return baseLanguage;
+  }
+  
+  /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#getResultTypesAndFeatures()
    */
   public TypeOrFeature[] getResultTypesAndFeatures() {
-    TypeOrFeature[] arr = new TypeOrFeature[mTypesAndFeatures.size()];
-    mTypesAndFeatures.keySet().toArray(arr);
+    TypeOrFeature[] arr = new TypeOrFeature[name2tof_langs.size()];
+    int i = 0;
+    for (ToF_Languages tof_langs : name2tof_langs.values()) {
+      arr[i++] = tof_langs.tof;
+    }
     return arr;
   }
+  
+  private Map<String, ToF_Languages> availName2tof_langs() {
+    if (needsCompilation) {
+      return name2tof_langs;
+    }
+    return withSubtypesName2tof_langs;
+  }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#getResultTypesAndFeatures(java.lang.String)
    */
   public TypeOrFeature[] getResultTypesAndFeatures(String language) {
-    // get language without country if applicable
-    String baseLanguage = null;
-    int index = language.indexOf(LANGUAGE_SEPARATOR);
-    if (index > -1) {
-      baseLanguage = language.substring(0, index);
-    }
+    
+    int languageIndex = getLanguageIndex(language);
+    int baseLanguageIndex = getLanguageWithoutCountryIndex(language);
 
     // holds the found ToFs for the specified language
-    Vector vec = new Vector();
+    List<TypeOrFeature> foundToF = new ArrayList<TypeOrFeature>();
 
-    Iterator it = mTypesAndFeatures.keySet().iterator();
-    while (it.hasNext()) {
-      // get current key
-      TypeOrFeature tof = (TypeOrFeature) it.next();
-      // get value for the current key
-      Set values = (HashSet) mTypesAndFeatures.get(tof);
-      if (values.contains(language) || values.contains(UNSPECIFIED_LANGUAGE)
-              || values.contains(baseLanguage)) {
-        // add tof to the TypeOfFeature array
-        vec.add(tof);
+    for (Map.Entry<String, ToF_Languages> entry : name2tof_langs.entrySet()) {
+      if (languageMatches(entry.getValue(), languageIndex, baseLanguageIndex)) {
+        foundToF.add(entry.getValue().tof);
+      }
+    }
+    return foundToF.toArray(new TypeOrFeature[foundToF.size()]);
+  }
+
+  // private helper functions
+  
+//  private boolean sameLanguages(String [] s, BitSet b) {
+//    if (s.length != b.cardinality()) {
+//      return false;
+//    }
+//    for (String lang : s) {
+//      if ( ! b.get(getLanguageIndex(lang))) {
+//        return false;
+//      }
+//    }
+//    return true;
+//  }
+  
+  /**
+   * change null languages to the unspecified language
+   * change a set of languages that includes the unspecified language to
+   *   just the unspecified language.  
+   *   This is OK when storing things into a result spec, since
+   *     the unspecified language will match any query.
+   *   This doesn't apply for querying because the queries only 
+   *     specify one language, not a set 
+   */
+  private String [] normalizeLanguages(String [] languages) {   
+    if (null == languages) {
+      return mDefaultLanguage;
+    } else {
+      for (String lang : languages) {
+        if (lang.equals(Language.UNSPECIFIED_LANGUAGE)) {
+          return mDefaultLanguage;
+        }
+      }
+    }
+    // normalization is expensive - so do this once as part of parsing capabilities
+//    int i = 0;
+//    for (String language : languages) {
+//      languages[i++] = normalizeLanguage(language);
+//    }
+    return languages;  
+  }
+  
+//  private String normalizeLanguage(String language) {
+//    String result = language.toLowerCase(Locale.ENGLISH);  // language specs are in English locale
+//    return result.replace('_', '-');
+//  }
+  
+  private void setNeedsCompilation() {
+    needsCompilation = true;
+    if (0 != withSubtypesName2tof_langs.size()) {
+      withSubtypesName2tof_langs.clear();
+    }
+  }
+  
+  private void addTypeOrFeatureInternal(TypeOrFeature tof, String[] languages) {
+    languages = normalizeLanguages(languages);
+    
+    ToF_Languages tof_langs = name2tof_langs.get(tof.getName());
+    if (null == tof_langs) {
+      name2tof_langs.put(tof.getName(), new ToF_Languages(tof, languages));
+      setNeedsCompilation();
+      return;
+    }
+    tof_langs.tof.setAllAnnotatorFeatures(tof.isAllAnnotatorFeatures());
+    BitSet langBitSet = tof_langs.languages;
+    langBitSet.clear();
+    for (String lang : languages) {
+      langBitSet.set(getLanguageIndex(lang));
+    }
+    setNeedsCompilation();
+  }
+
+  private TypeOrFeature createTypeOrFeature(String name, boolean isType, boolean aAllAnnotatorFeatures) {
+    TypeOrFeature r = new TypeOrFeature_impl();
+    r.setType(isType);
+    r.setName(name);
+    if (isType) {
+      r.setAllAnnotatorFeatures(aAllAnnotatorFeatures);
+    }
+    return r;
+  }
+
+  private void addResultTypeOrFeatureAddLanguage(String name, boolean isType, boolean allAnnotatorFeatures, String[] languages) {
+
+    ToF_Languages tof_langs = name2tof_langs.get(name);
+    
+    if (null == tof_langs) {
+      addTypeOrFeatureInternal(createTypeOrFeature(name, isType, allAnnotatorFeatures), languages);
+      setNeedsCompilation();
+      return;
+    }
+    
+    // tof_langs entry for this name exists, so update it
+    addResultTypeOrFeatureAddLanguageCommon(tof_langs, allAnnotatorFeatures, languages);
+  } 
+  
+  private void addResultTypeOrFeatureAddLanguage(TypeOrFeature tof, String[] languages) {
+
+    ToF_Languages tof_langs = name2tof_langs.get(tof.getName());
+    
+    if (null == tof_langs) {
+      addTypeOrFeatureInternal(tof, languages);
+      setNeedsCompilation();
+      return;
+    }
+    
+    addResultTypeOrFeatureAddLanguageCommon(tof_langs, tof.isAllAnnotatorFeatures(), languages);
+  }
+  
+  private void addResultTypeOrFeatureAddLanguageCommon(
+      ToF_Languages tof_langs, boolean allAnnotatorFeatures, String [] languages) {
+
+    // tof_langs entry for this name exists, so update it
+    if (allAnnotatorFeatures) {
+      if (!tof_langs.tof.isAllAnnotatorFeatures()) {
+        tof_langs.tof.setAllAnnotatorFeatures(true);
+        setNeedsCompilation();
+      }
+    }
+
+    // update the languages by adding the new languages passed in
+    languages = normalizeLanguages(languages);
+    BitSet langBitSet = tof_langs.languages;
+    
+    // "==" ok here due to normalizeLanguages call above
+    if (languages == mDefaultLanguage) {
+      if ( ! langBitSet.get(UNSPECIFIED_LANGUAGE_INDEX)) {
+        langBitSet.clear();
+        langBitSet.set(UNSPECIFIED_LANGUAGE_INDEX);
+        setNeedsCompilation();
+      }
+      return;
+    } 
+ 
+    // languages set already exists; add new ones to existing set
+    for (String lang : languages) {
+      langBitSet.set(getLanguageIndex(lang));
+    }
+    setNeedsCompilation();
+  }
+  
+  /**
+   * version used by compile to add subtypes
+   * @param aTypeName
+   * @param aAllAnnotatorFeatures
+   * @param languages
+   */
+  private void addResultType(String name, boolean allAnnotatorFeatures, BitSet languages) {
+    ToF_Languages tof_langs = withSubtypesName2tof_langs.get(name);
+    
+    if (null == tof_langs) {
+      withSubtypesName2tof_langs.put(
+          name, 
+          new ToF_Languages(createTypeOrFeature(name, true, allAnnotatorFeatures), (BitSet)languages.clone()));
+      return;
+    }
+
+    // tof_langs entry for this name exists, so update it
+    if (allAnnotatorFeatures) {
+      if (!tof_langs.tof.isAllAnnotatorFeatures()) {
+        tof_langs.tof.setAllAnnotatorFeatures(true);
       }
     }
 
-    // create array for return
-    TypeOrFeature[] arr = new TypeOrFeature[vec.size()];
-
-    // convert vector to TypeOrFeature[]
-    vec.toArray(arr);
-
-    return arr;
+    // update the languages by adding the new languages passed in
+    tof_langs.languages.or(languages); 
   }
 
+   
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#setResultTypesAndFeatures(org.apache.uima.analysis_engine.TypeOrFeature[])
    */
   public void setResultTypesAndFeatures(TypeOrFeature[] aTypesAndFeatures) {
-    mTypesAndFeatures.clear();
-    for (int i = 0; i < aTypesAndFeatures.length; i++) {
-      mTypesAndFeatures.put(aTypesAndFeatures[i], mDefaultLanguage);
-    }
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    setResultTypesAndFeatures(aTypesAndFeatures, mDefaultLanguage);
   }
-
+  
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#setResultTypesAndFeatures(org.apache.uima.analysis_engine.TypeOrFeature[],
    *      java.lang.String[])
    */
   public void setResultTypesAndFeatures(TypeOrFeature[] aTypesAndFeatures, String[] aLanguageIDs) {
-    if (aLanguageIDs != null) {
-      // create HashSet for the aLanguageIDs with the initial capacity of the aLanguageIDs size
-      HashSet languagesSupported = new HashSet(aLanguageIDs.length);
-      // add all supported languages to at HashSet
-      for (int x = 0; x < aLanguageIDs.length; x++) {
-        // add current language to the HashSet
-        languagesSupported.add(aLanguageIDs[x]);
-      }
-
-      mTypesAndFeatures.clear();
-      for (int i = 0; i < aTypesAndFeatures.length; i++) {
-        mTypesAndFeatures.put(aTypesAndFeatures[i], languagesSupported);
-      }
-      // revert to uncompiled state
-      mCompiledNameToLanguageMap.clear();
-    } else {
-      // if aLangugeIDs is null set ToFs for the default language
-      setResultTypesAndFeatures(aTypesAndFeatures);
-    }
-
+    name2tof_langs.clear();
+    for (TypeOrFeature tof : aTypesAndFeatures) {
+      name2tof_langs.put(tof.getName(), new ToF_Languages(tof, normalizeLanguages(aLanguageIDs)));
+    }    
+    setNeedsCompilation();
   }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature)
    */
   public void addResultTypeOrFeature(TypeOrFeature aTypeOrFeature) {
-    mTypesAndFeatures.put(aTypeOrFeature, mDefaultLanguage);
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    addTypeOrFeatureInternal(aTypeOrFeature, mDefaultLanguage);
   }
 
   /**
@@ -178,80 +466,30 @@
    *      java.lang.String[])
    */
   public void addResultTypeOrFeature(TypeOrFeature aTypeOrFeature, String[] aLanguageIDs) {
-    if (aLanguageIDs != null) {
-      // create HashSet for the aLanguageIDs with the initial capacity of the aLanguageIDs size
-      HashSet languagesSupported = new HashSet(aLanguageIDs.length);
-      // add all supported languages to at HashSet
-      for (int x = 0; x < aLanguageIDs.length; x++) {
-        // add current language to the HashSet
-        languagesSupported.add(aLanguageIDs[x]);
-      }
-
-      mTypesAndFeatures.put(aTypeOrFeature, languagesSupported);
-      // revert to uncompiled state
-      mCompiledNameToLanguageMap.clear();
-    } else {
-      // if aLangugeIDs is null add ToF with the default language
-      addResultTypeOrFeature(aTypeOrFeature);
-    }
+    addTypeOrFeatureInternal(aTypeOrFeature, aLanguageIDs);
   }
-
+  
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultType(java.lang.String,
    *      boolean)
    */
   public void addResultType(String aTypeName, boolean aAllAnnotatorFeatures) {
-    TypeOrFeature t = new TypeOrFeature_impl();
-    t.setType(true);
-    t.setName(aTypeName);
-    t.setAllAnnotatorFeatures(aAllAnnotatorFeatures);
-    mTypesAndFeatures.put(t, mDefaultLanguage);
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    addTypeOrFeatureInternal(createTypeOrFeature(aTypeName, true, aAllAnnotatorFeatures), mDefaultLanguage);
   }
-
+  
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultType(java.lang.String,
    *      boolean, java.lang.String[])
    */
   public void addResultType(String aTypeName, boolean aAllAnnotatorFeatures, String[] aLanguageIDs) {
-    if (aLanguageIDs != null) {
-      // create HashSet for the aLanguageIDs with the initial capacity of the aLanguageIDs size
-      HashSet languagesSupported = new HashSet(aLanguageIDs.length);
-      // add all supported languages to at HashSet
-      for (int x = 0; x < aLanguageIDs.length; x++) {
-        // add current language to the HashSet
-        languagesSupported.add(aLanguageIDs[x]);
-      }
-
-      TypeOrFeature t = new TypeOrFeature_impl();
-      t.setType(true);
-      t.setName(aTypeName);
-      t.setAllAnnotatorFeatures(aAllAnnotatorFeatures);
-      HashSet existingLanguages = (HashSet) mTypesAndFeatures.get(t);
-      if (existingLanguages != null) {
-        existingLanguages.addAll(languagesSupported);
-      } else {
-        mTypesAndFeatures.put(t, languagesSupported);
-      }
-      // revert to uncompiled state
-      mCompiledNameToLanguageMap.clear();
-    } else {
-      // if aLangugeIDs is null add type with the default language
-      addResultType(aTypeName, aAllAnnotatorFeatures);
-    }
+    addResultTypeOrFeatureAddLanguage(aTypeName, true, aAllAnnotatorFeatures, aLanguageIDs);
   }
-
+  
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultFeature(java.lang.String)
    */
   public void addResultFeature(String aFullFeatureName) {
-    TypeOrFeature f = new TypeOrFeature_impl();
-    f.setType(false);
-    f.setName(aFullFeatureName);
-    mTypesAndFeatures.put(f, mDefaultLanguage);
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    addTypeOrFeatureInternal(createTypeOrFeature(aFullFeatureName, false, false), mDefaultLanguage);
   }
 
   /**
@@ -259,76 +497,110 @@
    *      java.lang.String[])
    */
   public void addResultFeature(String aFullFeatureName, String[] aLanguageIDs) {
-    if (aLanguageIDs != null) {
-      // create HashSet for the aLanguageIDs with the initial capacity of the aLanguageIDs size
-      HashSet languagesSupported = new HashSet(aLanguageIDs.length);
-      // add all supported languages to at HashSet
-      for (int x = 0; x < aLanguageIDs.length; x++) {
-        // add current language to the HashSet
-        languagesSupported.add(aLanguageIDs[x]);
-      }
-
-      TypeOrFeature f = new TypeOrFeature_impl();
-      f.setType(false);
-      f.setName(aFullFeatureName);
-      HashSet existingLanguages = (HashSet) mTypesAndFeatures.get(f);
-      if (existingLanguages != null) {
-        existingLanguages.addAll(languagesSupported);
-      } else {
-        mTypesAndFeatures.put(f, languagesSupported);
-      }
-      // revert to uncompiled state
-      mCompiledNameToLanguageMap.clear();
-    } else {
-      // if aLangugeIDs is null add type with the default language
-      addResultFeature(aFullFeatureName);
-    }
+    addResultTypeOrFeatureAddLanguage(aFullFeatureName, false, false, aLanguageIDs);
   }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#compile(org.apache.uima.cas.TypeSystem)
    */
   public void compile(TypeSystem aTypeSystem) {
-    mCompiledNameToLanguageMap.clear();
-    Iterator it = mTypesAndFeatures.entrySet().iterator();
-    while (it.hasNext()) {
-      Map.Entry elem = (Map.Entry) it.next();
-      TypeOrFeature tof = (TypeOrFeature) elem.getKey();
-      if (tof.isType()) {
-        Type t = aTypeSystem.getType(tof.getName());
-        if (t != null) {
-          addTypeRecursive(t, aTypeSystem, (HashSet) elem.getValue(), tof.isAllAnnotatorFeatures());
+    setTypeSystem(aTypeSystem);
+    compileIfNeeded();
+  }
+  
+//  private static class TypeToCompile {
+//    String name;
+//    boolean allFeatures;
+//    String[] languages;
+//    TypeToCompile(String aName, boolean aAllFeatures, String[] aLanguages) {
+//      name = aName;
+//      allFeatures = aAllFeatures;
+//      languages = aLanguages;
+//    }
+//  }
+  
+  private void compile() { 
+    if (null == mTypeSystem) {
+      return;
+    }
+    
+    needsCompilation = false;
+    // get set of current type names
+    // for each name, get set of implied additional names (allAnnotatorFeatures and subtypes), recursively
+    // add with languages
+    
+    // issue:  can a result spec hold for language 1 types a b c, for language 2 types a b? yes
+    //         can it hold for lang 1 type a(allfeats) and for lang 2 type a(not all feat)? no
+    
+//    Map<String, TypeToCompile> typesToCompile = new HashMap<String, TypeToCompile>(mNameToTofLang.size());
+//    for (ToF_Languages tof_langs : mNameToTofLang.values()) {
+//      TypeOrFeature tof = tof_langs.tof;
+//      if (tof.isType()) {
+//        String typeName = tof.getName();
+//        typesToCompile.put(typeName, new TypeToCompile(typeName, tof.isAllAnnotatorFeatures(), tof_langs.languages));
+//      }
+//    }
+
+    for (ToF_Languages tof_langs : name2tof_langs.values()) {
+        TypeOrFeature tof = tof_langs.tof;
+        // copy type or feature to other map
+        withSubtypesName2tof_langs.put(tof.getName(), (ToF_Languages) tof_langs.clone());
+        if (tof.isType()) {
+          compileTypeRecursively(mTypeSystem.getType(tof.getName()), tof.isAllAnnotatorFeatures(), tof_langs.languages);
         }
-      } else // feature
-      {
-        mCompiledNameToLanguageMap.put(tof.getName(), elem.getValue());
-      }
     }
-    // TODO: process the set of intersections
   }
-
-  /**
-   * @param t
-   */
-  private void addTypeRecursive(Type type, TypeSystem typeSystem, HashSet languages,
-          boolean allFeatures) {
-    mCompiledNameToLanguageMap.put(type.getName(), languages);
-    if (allFeatures) {
-      List features = type.getFeatures();
-      Iterator featIt = features.iterator();
-      while (featIt.hasNext()) {
-        Feature f = (Feature) featIt.next();
-        mCompiledNameToLanguageMap.put(f.getName(), languages);
+    
+//    mCompiledNameToLanguageMap.clear();
+//    for (Map.Entry<TypeOrFeature, Set<String>> elem : mTypesAndFeatures.entrySet()) {
+//      TypeOrFeature tof = elem.getKey();
+//      if (tof.isType()) {
+//        Type t = aTypeSystem.getType(tof.getName());
+//        if (t != null) {
+//          addTypeRecursive(t, aTypeSystem, elem.getValue(), tof.isAllAnnotatorFeatures());
+//        }
+//      } else { // feature
+//        mCompiledNameToLanguageMap.put(tof.getName(), elem.getValue());
+//      }
+//    }
+//    // TODO: process the set of intersections
+//  }
+
+  private void compileTypeRecursively(Type type, boolean allFeatures, BitSet languages) {
+
+    if (null != type) {
+//      if (allFeatures) {
+//        for (Feature f : (List<Feature>) type.getFeatures()) {
+//          addResultFeature(f.getName(), languages); // this add "merges"
+//                                                    // langauges with existing
+//                                                    // ones
+//        }
+//      }
+      
+      for (Type subType : (List<Type>) mTypeSystem.getDirectSubtypes(type)) {
+        String subTypeName = subType.getName();
+        addResultType(subTypeName, allFeatures, languages);
+        compileTypeRecursively(subType, allFeatures, languages);
       }
     }
-    // recurse on subtypes
-    List subtypes = typeSystem.getDirectSubtypes(type);
-    Iterator typeIt = subtypes.iterator();
-    while (typeIt.hasNext()) {
-      Type subtype = (Type) typeIt.next();
-      addTypeRecursive(subtype, typeSystem, languages, allFeatures);
-    }
   }
+  
+// /**
+// * @param t
+// */
+//  private void addTypeRecursive(Type type, TypeSystem typeSystem, Set<String> languages,
+//          boolean allFeatures) {
+//    mCompiledNameToLanguageMap.put(type.getName(), languages);
+//    if (allFeatures) {
+//      for (Feature f : (List<Feature>)type.getFeatures()) {
+//        mCompiledNameToLanguageMap.put(f.getName(), languages);
+//      }
+//    }
+//    // recurse on subtypes
+//    for (Type subtype : (List<Type>)typeSystem.getDirectSubtypes(type)) {
+//      addTypeRecursive(subtype, typeSystem, languages, allFeatures);
+//    }
+//  }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#containsType(java.lang.String)
@@ -336,171 +608,123 @@
   public boolean containsType(String aTypeName) {
     if (aTypeName.indexOf(TypeSystem.FEATURE_SEPARATOR) != -1)
       return false; // check against someone passing a feature name here
-    // if compile() has been called can be done by a hash lookup
-    if (!mCompiledNameToLanguageMap.isEmpty()) {
-      return mCompiledNameToLanguageMap.containsKey(aTypeName);
-    } else {
-      // brute force search
-      Iterator it = mTypesAndFeatures.keySet().iterator();
-      while (it.hasNext()) {
-        TypeOrFeature elem = (TypeOrFeature) it.next();
-        if (elem.isType() && aTypeName.equals(elem.getName())) {
-          return true;
-        }
-      }
-      return false;
-    }
+    compileIfNeeded();
+    return availName2tof_langs().containsKey(aTypeName);
   }
+    
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#containsType(java.lang.String,java.lang.String)
    */
   public boolean containsType(String aTypeName, String language) {
-    if (language == null) {
+    if (language == null || language.equals(Language.UNSPECIFIED_LANGUAGE)) {
       return containsType(aTypeName);
     }
     if (aTypeName.indexOf(TypeSystem.FEATURE_SEPARATOR) != -1)
       return false; // check against someone passing a feature name here
-
-    // get language without country if applicable
-    String baseLanguage = null;
-    int index = language.indexOf(LANGUAGE_SEPARATOR);
-    if (index > -1) {
-      baseLanguage = language.substring(0, index);
-    }
-
-    boolean found = false;
-
-    if (!mCompiledNameToLanguageMap.isEmpty()) {
-      // if compile() has been called can be done by a hash lookup
-      HashSet languages = (HashSet) mCompiledNameToLanguageMap.get(aTypeName);
-      if (languages != null) {
-        // check if tof is supported for the current language
-        if (UNSPECIFIED_LANGUAGE.equals(language) || languages.contains(language)
-                || languages.contains(UNSPECIFIED_LANGUAGE) || languages.contains(baseLanguage)) {
-          // mark item found
-          found = true;
-        }
-      }
-    } else {
-      // brute force search
-      Iterator it = mTypesAndFeatures.keySet().iterator();
-      while (it.hasNext()) {
-        TypeOrFeature elem = (TypeOrFeature) it.next();
-        if (elem.isType() && aTypeName.equals(elem.getName())) {
-          HashSet languages = (HashSet) mTypesAndFeatures.get(elem);
-          if (languages != null) {
-            // check if tof is supported for the current language
-            if (UNSPECIFIED_LANGUAGE.equals(language) || languages.contains(language)
-                    || languages.contains(UNSPECIFIED_LANGUAGE) || languages.contains(baseLanguage)) {
-              // mark item found
-              return true;
-            }
-          }
-        }
-      }
-    }
-
-    return found;
+    
+    compileIfNeeded();
+    return languageMatches(availName2tof_langs().get(aTypeName), language);
   }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#containsFeature(java.lang.String)
    */
   public boolean containsFeature(String aFullFeatureName) {
-    if (aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR) == -1)
+    int typeEndPosition = aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR);
+    if (typeEndPosition == -1)
       return false; // check against someone passing a type name here
 
-    // if compile() has been called can be done by a hash lookup
-    if (!mCompiledNameToLanguageMap.isEmpty()) {
-      return mCompiledNameToLanguageMap.containsKey(aFullFeatureName);
-    } else {
-      // brute force search
-      // (also need to consider Types with allAnnotatorFeatures = true)
-      String typeName = "";
-      int typeSeparatorIndex = aFullFeatureName.indexOf(':');
-      if (typeSeparatorIndex > 0) {
-        typeName = aFullFeatureName.substring(0, typeSeparatorIndex);
-      }
-
-      Iterator it = mTypesAndFeatures.keySet().iterator();
-      while (it.hasNext()) {
-        TypeOrFeature elem = (TypeOrFeature) it.next();
-        if ((!elem.isType() && aFullFeatureName.equals(elem.getName()))
-                || (elem.isType() && elem.isAllAnnotatorFeatures() && typeName.equals(elem
-                        .getName()))) {
-          return true;
-        }
+    compileIfNeeded();
+    if (availName2tof_langs().containsKey(aFullFeatureName)) {
+      return true;
+    }
+    
+    // special code here to return true if the allAnnotatorFeatures flag is set for the type
+    String typeName = aFullFeatureName.substring(0, typeEndPosition);
+    ToF_Languages tof_langs = availName2tof_langs().get(typeName);
+    if (null != tof_langs && tof_langs.tof.isAllAnnotatorFeatures()) {
+      if (null != mTypeSystem) {
+        return null != mTypeSystem.getFeatureByFullName(aFullFeatureName);  // verify feature is there
       }
-
-      return false;
+      return true;
     }
+    return false;
   }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#containsFeature(java.lang.String,java.lang.String)
    */
   public boolean containsFeature(String aFullFeatureName, String language) {
-    if (language == null) {
+    if (language == null || language.equals(Language.UNSPECIFIED_LANGUAGE)) {
       return containsFeature(aFullFeatureName);
     }
-    if (aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR) == -1)
+    int typeEndPosition = aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR);
+    if (typeEndPosition == -1)
       return false; // check against someone passing a type name here
 
-    // get language without country if applicable
-    String baseLanguage = null;
-    int index = language.indexOf(LANGUAGE_SEPARATOR);
-    if (index > -1) {
-      baseLanguage = language.substring(0, index);
-    }
+    compileIfNeeded();
+    ToF_Languages tof_langs = name2tof_langs.get(aFullFeatureName);
+    if (languageMatches(tof_langs, language)) {
+      return true;
+    }
+    
+    // special code for allAnnotatorFeatures: return true if type name is found and
+    // has all annotator features set
+    tof_langs = availName2tof_langs().get(aFullFeatureName.substring(0, typeEndPosition));
+    if (null != tof_langs && tof_langs.tof.isAllAnnotatorFeatures() && languageMatches(tof_langs, language)) {
+      if (null != mTypeSystem) {
+        return null != mTypeSystem.getFeatureByFullName(aFullFeatureName);  // verify feature is there
+      }
+      return true;
+    }
+    return false;
+  }
 
-    boolean found = false;
+  /**
+   * Languages matches if the query language is xxx-yyy and
+   *    result spec languages contains:
+   *       x-unspecified
+   *       xxx-yyy
+   *       xxx  
+   *    or if query language is x-unspecified
+   *    
+   * @param tof_langs
+   * @param language
+   * @return
+   */
+  private boolean languageMatches(ToF_Languages tof_langs, String language) {
+    if (null == tof_langs) {
+      return false;
+    }    
+    BitSet languages = tof_langs.languages;
+    if (languages.get(UNSPECIFIED_LANGUAGE_INDEX) ||
+        languages.get(getLanguageIndex(language))) {
+      return true;
+    }
+    String baseLanguage = getLanguageWithoutCountry(language);
+    return baseLanguage != language && 
+           languages.get(getLanguageIndex(baseLanguage));
+  }
 
-    // if compile() has been called can be done by a hash lookup
-    if (!mCompiledNameToLanguageMap.isEmpty()) {
-      HashSet languages = (HashSet) mCompiledNameToLanguageMap.get(aFullFeatureName);
-      if (languages != null) {
-        // check if tof is supported for the current language
-        if (UNSPECIFIED_LANGUAGE.equals(language) || languages.contains(language)
-                || languages.contains(UNSPECIFIED_LANGUAGE) || languages.contains(baseLanguage)) {
-          // mark item found
-          found = true;
-        }
-      }
-    } else {
-      // brute force search
-      // (also need to consider Types with allAnnotatorFeatures = true)
-      String typeName = "";
-      int typeSeparatorIndex = aFullFeatureName.indexOf(':');
-      if (typeSeparatorIndex > 0) {
-        typeName = aFullFeatureName.substring(0, typeSeparatorIndex);
-      }
-
-      Iterator it = mTypesAndFeatures.keySet().iterator();
-      while (it.hasNext()) {
-        TypeOrFeature elem = (TypeOrFeature) it.next();
-        if ((!elem.isType() && aFullFeatureName.equals(elem.getName()))
-                || (elem.isType() && elem.isAllAnnotatorFeatures() && typeName.equals(elem
-                        .getName()))) {
-          HashSet languages = (HashSet) mTypesAndFeatures.get(elem);
-          if (languages != null) {
-            // check if tof is supported for the current language
-            if (UNSPECIFIED_LANGUAGE.equals(language) || languages.contains(language)
-                    || languages.contains(UNSPECIFIED_LANGUAGE) || languages.contains(baseLanguage)) {
-              // mark item found
-              return true;
-            }
-          }
-        }
-      }
+  private boolean languageMatches(ToF_Languages tof_langs, int languageIndex, int baseLanguageIndex) {
+    if (null == tof_langs) {
+      return false;
+    }    
+    BitSet languages = tof_langs.languages;
+    if (languages.get(UNSPECIFIED_LANGUAGE_INDEX) ||
+        languages.get(languageIndex)) {
+      return true;
     }
-
-    return found;
+    return baseLanguageIndex != languageIndex && 
+           languages.get(baseLanguageIndex);
   }
+  
 
   /**
    * @see org.apache.uima.resource.impl.MetaDataObject_impl#getXmlizationInfo()
    */
+  @Override
   protected XmlizationInfo getXmlizationInfo() {
     return new XmlizationInfo("resultSpecification", null,
             new PropertyXmlInfo[] { new PropertyXmlInfo("resultTypesAndFeatures", null) });
@@ -518,45 +742,29 @@
    *      boolean)
    */
   public void addCapabilities(Capability[] capabilities, boolean outputs) {
-    if (capabilities != null) {
-      for (int i = 0; i < capabilities.length; i++) {
-        TypeOrFeature[] tofs = outputs ? capabilities[i].getOutputs() : capabilities[i].getInputs();
-
-        // get supported languages
-        String[] supportedLanguagesArr = capabilities[i].getLanguagesSupported();
-
-        HashSet supportedLanguages = null;
-        if (supportedLanguagesArr != null && supportedLanguagesArr.length > 0) {
-          // create new HashSet with the initial capacity of the supportedLanguageArr
-          supportedLanguages = new HashSet(supportedLanguagesArr.length);
-          // add all supported languages to at HashSet
-          for (int x = 0; x < supportedLanguagesArr.length; x++) {
-            // add current language to the HashSet
-            supportedLanguages.add(supportedLanguagesArr[x]);
-          }
-        } else {
-          // if no languages are set, set default language
-          supportedLanguages = mDefaultLanguage;
-        }
-
-        for (int y = 0; y < tofs.length; y++) {
-          mTypesAndFeatures.put(tofs[y], supportedLanguages);
-        }
-
+    if (null == capabilities) {
+      return;
+    }
+    for (Capability capability : capabilities) {
+      TypeOrFeature[] tofs = outputs ? capability.getOutputs() : capability.getInputs();
+      String[] supportedLanguages = capability.getLanguagesSupported();
+      if (null == supportedLanguages) {
+        supportedLanguages = mDefaultLanguage;
+      }
+      for (TypeOrFeature tof : tofs) {
+        addResultTypeOrFeatureAddLanguage(tof, supportedLanguages);
       }
     }
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    setNeedsCompilation();
   }
 
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#removeTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature)
    */
   public void removeTypeOrFeature(TypeOrFeature aTypeOrFeature) {
-    // reomve Type or Feature from the
-    mTypesAndFeatures.remove(aTypeOrFeature);
-    // revert to uncompiled state
-    mCompiledNameToLanguageMap.clear();
+    // remove Type or Feature from the
+    name2tof_langs.remove(aTypeOrFeature.getName());
+    setNeedsCompilation();  // may have removed something which had subtypes
   }
 
   /**
@@ -564,18 +772,26 @@
    * 
    * @return Object copy of the current object
    */
+  @Override
   public Object clone() {
     // create new result specification
     // NOTE: we don't use super.clone here, since for performance reasons
     // we want to do a faster clone that what the general-purpose logic in
     // MetaDataObject_impl does. This class is marked final so that
     // this can't cause a problem if ResultSpecification_impl is subclassed.
-    ResultSpecification_impl newResultSpec = new ResultSpecification_impl();
-
-    // clone HashMaps
-    newResultSpec.mTypesAndFeatures = new HashMap(this.mTypesAndFeatures);
-    newResultSpec.mDefaultLanguage = new HashSet(this.mDefaultLanguage);
+    return new ResultSpecification_impl(this);
+  }
 
-    return newResultSpec;
+  public void setTypeSystem(TypeSystem ts) {
+    if (mTypeSystem == ts) {
+      return;
+    }
+    mTypeSystem = ts;
+    setNeedsCompilation();
+  }
+  
+  public TypeSystem getTypeSystem() {
+    return mTypeSystem;
   }
+
 }

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/compatibility/AnnotatorAdapter.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/compatibility/AnnotatorAdapter.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/compatibility/AnnotatorAdapter.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/compatibility/AnnotatorAdapter.java Sun Jan 27 06:21:40 2008
@@ -139,12 +139,15 @@
       if (mAnnotator instanceof TextAnnotator) {
         CAS cas = (CAS) aCAS;
         ResultSpecification rs = getResultSpecForLanguage(cas.getDocumentLanguage());
+        rs.setTypeSystem(cas.getTypeSystem());
         ((TextAnnotator) mAnnotator).process(cas, rs);
       } else if (mAnnotator instanceof JTextAnnotator) {
         JCas jcas = (JCas) aCAS;
         ResultSpecification rs = getResultSpecForLanguage(jcas.getDocumentLanguage());
+        rs.setTypeSystem(jcas.getTypeSystem());
         ((JTextAnnotator) mAnnotator).process(jcas, rs);
       } else if (mAnnotator instanceof GenericAnnotator) {
+        mDefaultResultSpecification.setTypeSystem(((CAS) aCAS).getTypeSystem());
         ((GenericAnnotator) mAnnotator).process((CAS) aCAS, mDefaultResultSpecification);
       }
     } catch (AnnotatorProcessException e) {

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowController.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowController.java?rev=615608&r1=615607&r2=615608&view=diff
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowController.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/CapabilityLanguageFlowController.java Sun Jan 27 06:21:40 2008
@@ -24,9 +24,9 @@
 import java.util.ArrayList;
 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 org.apache.uima.UIMAFramework;
 import org.apache.uima.UIMARuntimeException;
@@ -52,11 +52,14 @@
  * have already been produced by a previous AE in the flow.
  */
 public class CapabilityLanguageFlowController extends CasFlowController_ImplBase {
-  private ArrayList mStaticSequence;
+  private List<AnalysisSequenceCapabilityNode> mStaticSequence;
 
-  private Map mComponentMetaDataMap;
+  private Map<String, AnalysisEngineMetaData> mComponentMetaDataMap;
 
-  private Map mFlowTable;
+  private Map<String, List<AnalysisSequenceCapabilityNode>> mFlowTable;
+  
+  private final Map<String, ResultSpecification> lastResultSpecForComponent = 
+    new HashMap<String, ResultSpecification>();
 
   /**
    * main language separator e.g 'en' and 'en-US'
@@ -70,16 +73,18 @@
    */
   public void initialize(FlowControllerContext aContext) throws ResourceInitializationException {
     super.initialize(aContext);
-    mComponentMetaDataMap = aContext.getAnalysisEngineMetaDataMap();
+    mComponentMetaDataMap = (Map<String, AnalysisEngineMetaData>)aContext.getAnalysisEngineMetaDataMap();
 
     // build a list of AnalysisSequenceNodes from the capabilityLanguageFlow
-    mStaticSequence = new ArrayList();
+    mStaticSequence = new ArrayList<AnalysisSequenceCapabilityNode>();
     CapabilityLanguageFlow flowConstraints = (CapabilityLanguageFlow) aContext
             .getAggregateMetadata().getFlowConstraints();
-    String[] flow = flowConstraints.getCapabilityLanguageFlow();
-    for (int i = 0; i < flow.length; i++) {
-      AnalysisEngineMetaData md = (AnalysisEngineMetaData) mComponentMetaDataMap.get(flow[i]);
-      mStaticSequence.add(new AnalysisSequenceCapabilityNode(flow[i], md.getCapabilities(), null));
+    for (String aeKey : flowConstraints.getCapabilityLanguageFlow()) {
+      mStaticSequence.add(
+          new AnalysisSequenceCapabilityNode(
+              aeKey, 
+              mComponentMetaDataMap.get(aeKey).getCapabilities(), 
+              null));
     }
 
     // compute flow table with the specified capabilities
@@ -92,43 +97,37 @@
    * @see org.apache.uima.flow.CasFlowController_ImplBase#computeFlow(org.apache.uima.cas.CAS)
    */
   public Flow computeFlow(CAS aCAS) throws AnalysisEngineProcessException {
-    CapabilityLanguageFlowObject flow = new CapabilityLanguageFlowObject(mFlowTable);
+    CapabilityLanguageFlowObject flow = new CapabilityLanguageFlowObject(mFlowTable, this);
     flow.setCas(aCAS);
     return flow;
   }
 
   /**
-   * method computeFlowTable create the flow table for faster processing. The flow table inlcudes
-   * for all languages in the capabilities the coresponding flow sequence
+   * method computeFlowTable create the flow table for faster processing. The flow table includes
+   * the coresponding flow sequence for all languages in the capabilities 
    * 
    * @param aCapabilities
    *          aggregate engine capabilities
    * @return Map - flow table includes all sequences for all languages
    */
-  protected Map computeFlowTable(Capability[] aCapabilities) {
+  protected Map<String, List<AnalysisSequenceCapabilityNode>> computeFlowTable(Capability[] aCapabilities) {
     // create flowTable
-    Map flowTable = new HashMap();
+    Map<String, List<AnalysisSequenceCapabilityNode>> flowTable = 
+      new HashMap<String, List<AnalysisSequenceCapabilityNode>>();
 
     // get all languages from the capabilities
-    HashSet languages = new HashSet();
-    for (int i = 0; i < aCapabilities.length; i++) {
-      // get languages from current capability
-      aCapabilities[i].getLanguagesSupported();
-      String language;
-      for (int y = 0; y < aCapabilities[i].getLanguagesSupported().length; y++) {
-        language = aCapabilities[i].getLanguagesSupported()[y];
-        languages.add(language);
+    Set<String> languages = new HashSet<String>();
+    for (Capability capability : aCapabilities) {
+      for (String capabilityLanguage : capability.getLanguagesSupported()) {
+        languages.add(capabilityLanguage);
       }
     }
 
     // create flow table with sequences for all languages
-    Iterator it = languages.iterator();
-    while (it.hasNext()) {
-      // add sequence for the current language
-      String language = (String) it.next();
-      flowTable.put(language, computeSequence(language, aCapabilities));
+    for (String capabilityLanguage : languages) {
+      flowTable.put(capabilityLanguage, computeSequence(capabilityLanguage, aCapabilities));
     }
-
+    
     return flowTable;
   }
 
@@ -142,8 +141,8 @@
    * 
    * @return List - capabilityLanguageAnalysisSequence for the current language
    */
-  protected List computeSequence(String language, Capability[] aCapabilities) {
-    language = Language.normalize(language);
+  protected List<AnalysisSequenceCapabilityNode> computeSequence(String language, Capability[] aCapabilities) {
+    language = Language.normalize(language);  // lower-cases, replaces _ with -, changes null to x-unspecified
 
     // create resultSpec from the current aggregate capabilities
     ResultSpecification resultSpec = UIMAFramework.getResourceSpecifierFactory()
@@ -156,43 +155,48 @@
     }
 
     // create array list for the current sequence
-    List newSequence = new ArrayList();
+    List<AnalysisSequenceCapabilityNode> newSequence = new ArrayList<AnalysisSequenceCapabilityNode>();
 
-    // loop pver all annotators that should be called
+    // loop over all annotators that should be called
+    // In this loop we will gradually reduce the set of output capabilities 
     for (int sequenceIndex = 0; sequenceIndex < mStaticSequence.size(); sequenceIndex++) {
-      // get array of ouput capabilities for the current languge from the current result spec
-      TypeOrFeature[] ouputCapabilities = resultSpec.getResultTypesAndFeatures(language);
+      // get array of output capabilities for the current language from the current result spec
+      TypeOrFeature[] outputCapabilities = resultSpec.getResultTypesAndFeatures(language);
 
+      // Augment these outputCapabilities if the language-spec is for a country, to 
+      // include the outputCapabilities for the language without the country-spec.
+      
       // strip language extension if available
       int index = language.indexOf(LANGUAGE_SEPARATOR);
 
       // if country extension is available
       if (index >= 0) {
-        // create HashSet for outputSpec
-        HashSet outputSpec = new HashSet();
+        // create Set for outputSpecs, so we can eliminate duplicates
+        Set<TypeOrFeature> outputSpec = new HashSet<TypeOrFeature>();
 
-        // add language with country extension output capabilities to the outputSpec
-        if (ouputCapabilities.length > 0) {
-          for (int i = 0; i < ouputCapabilities.length; i++) {
-            outputSpec.add(ouputCapabilities[i]);
+        // add language with country extension removed, 
+        // to the existing output capabilities (or if non exist, just use
+        // the capabilities for the language without the country extension)
+        if (outputCapabilities.length > 0) {
+          // copy all existing capabilities to the Set
+          for (TypeOrFeature outputCapability : outputCapabilities) {
+            outputSpec.add(outputCapability);
           }
 
           // get array of output capabilities only for the language without country extension
-          ouputCapabilities = resultSpec.getResultTypesAndFeatures(language.substring(0, index));
+          outputCapabilities = resultSpec.getResultTypesAndFeatures(language.substring(0, index));
 
-          // add language output capabilities to the outputSpec
-          for (int i = 0; i < ouputCapabilities.length; i++) {
-            outputSpec.add(ouputCapabilities[i]);
+          // add language output capabilities to the Set
+          for (TypeOrFeature outputCapability : outputCapabilities) {
+            outputSpec.add(outputCapability);
           }
 
           // convert all output capabilities to a outputCapabilities array
-          ouputCapabilities = new TypeOrFeature[outputSpec.size()];
-          outputSpec.toArray(ouputCapabilities);
-        } else
-        // for language with country extension was noting found
-        {
+          outputCapabilities = new TypeOrFeature[outputSpec.size()];
+          outputSpec.toArray(outputCapabilities);
+        } else { // for language with country extension was noting found        
           // get array of output capabilities with the new main language without country extension
-          ouputCapabilities = resultSpec.getResultTypesAndFeatures(language.substring(0, index));
+          outputCapabilities = resultSpec.getResultTypesAndFeatures(language.substring(0, index));
         }
       }
 
@@ -208,7 +212,7 @@
       // check output capabilites from the current result spec
 
       // get next analysis engine from the sequence node
-      node = (AnalysisSequenceCapabilityNode) mStaticSequence.get(sequenceIndex);
+      node = mStaticSequence.get(sequenceIndex);
 
       // get capability container from the current analysis engine
       CapabilityContainer capabilityContainer = node.getCapabilityContainer();
@@ -217,33 +221,39 @@
       currentAnalysisResultSpec = UIMAFramework.getResourceSpecifierFactory()
               .createResultSpecification();
 
-      // check if engine should be called - loop over all ouput capabilities of the result spec
-      for (int i = 0; i < ouputCapabilities.length; i++) {
-        // check if current ToF can be produced by the current analysis engine
-        if (capabilityContainer.hasOutputTypeOrFeature(ouputCapabilities[i], language, true)) {
-          currentAnalysisResultSpec.addResultTypeOrFeature(ouputCapabilities[i]);
+      // check if engine should be called - 
+      //   loop over all remaining ouput capabilities of the aggregate's result spec
+      //     to see if this component of the aggregate produces that type or feature,
+      //     for this language
+      for (TypeOrFeature tof : outputCapabilities) {
+        if (capabilityContainer.hasOutputTypeOrFeature(tof, language, true)) {
+          currentAnalysisResultSpec.addResultTypeOrFeature(tof);
           shouldEngineBeCalled = true;
-
           // remove current ToF from the result spec
-          resultSpec.removeTypeOrFeature(ouputCapabilities[i]);
+          resultSpec.removeTypeOrFeature(tof);
         }
       }
+      
       // skip engine if not output capability match
 
-      // check if current engine should be called
+      // should be called is false if this engine produces none of the 
+      //   needed outputs of the aggregate
       if (shouldEngineBeCalled == true) {
-        // set result spec for current analysis engine
+        // tell this component which output types/features need to be produced
+        //   note: As an exception to the way normal result-specifications are produced,
+        //         here we *don't* add the types/features which are input to
+        //         other delegates need to be produced.
+        //         This is for backward compatibility.
         node.setResultSpec(currentAnalysisResultSpec);
 
         // add note to the current sequence
-        newSequence.add(node.clone());
-      } else
+        newSequence.add((AnalysisSequenceCapabilityNode)node.clone());
+      } else {
       // engine should not be called, but add null to the sequence to track that
       // engine should not be called
-      {
         newSequence.add(null);
       }
-    }
+    } // loop over all delegates in the flow sequence
 
     return newSequence;
   }
@@ -261,5 +271,9 @@
       throw new UIMARuntimeException(e);
     }
     return desc;
+  }
+
+  public Map<String, ResultSpecification> getLastResultSpecForComponent() {
+    return lastResultSpecForComponent;
   }
 }