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 2010/08/02 19:51:33 UTC

svn commit: r981642 - in /uima/uimaj/trunk/uimaj-core/src: main/java/org/apache/uima/analysis_engine/ main/java/org/apache/uima/analysis_engine/impl/ test/java/org/apache/uima/analysis_engine/impl/

Author: schor
Date: Mon Aug  2 17:51:33 2010
New Revision: 981642

URL: http://svn.apache.org/viewvc?rev=981642&view=rev
Log:
[UIMA-1840] ResultSpecification_impl has new static method that takes 2 ResultSpec_impls and intersects them, following language subsumption rules.  The Javadocs for containsType and containsFeature are clarified to represent what they do in all the specific cases of language subsumption.  The PrimitiveAnalysisEngine_impl is modified to cache the resultSpec corresponding to its output capabilities, everytime the type system changes. The call to do the intersection is changed to use the new method in the ResutlSpecification_impl.  The old method, computeAnalysisComponentResultSpec, is commented out.  I hope no users made use of this (we'll soon see ...).  I made a new set of test cases for this (ResultSpecTest), and moved the resultSpec test from the AnalysisEngine_implTest, and modified it to work (it needs the type system specified, in order to compute the intersection for these tests).

Added:
    uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/ResultSpecTest.java
Modified:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java
    uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java?rev=981642&r1=981641&r2=981642&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/ResultSpecification.java Mon Aug  2 17:51:33 2010
@@ -279,12 +279,21 @@ public interface ResultSpecification ext
    * @param aTypeName
    *          the name of the type
    * @param aLanguage
-   *          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.
+   *          the language to search for.  
+   *          A null value or the value x-unspecified for this argument only
+   *            matches ResultSpecifications having x-unspecified as their type.
+   *          A language value that is contained within a language in the ResultSpecification
+   *            is considered to match.  In particular:
+   *               Language          ResultSpecification      Result
+   *               x-unspecified     x-unspecified             match
+   *               x-unspecified     en                        no match
+   *               en                x-unspecified             match
+   *               en                en-us                     no match
+   *               en-us             en                        match
+   *               
    * 
    * @return true if and only if this <code>ResultSpecification</code> contains the type with name
-   *         <code>aTypeName</code> for the specified language.
+   *         <code>aTypeName</code> for a matching language.
    */
   public boolean containsType(String aTypeName, String aLanguage);
 
@@ -315,12 +324,20 @@ public interface ResultSpecification ext
    * @param aFullFeatureName
    *          the fully-qualified name of the feature, in the form MyTypeName:MyFeatureName.
    * @param aLanguage
-   *          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.
+   *          the language to search for.  
+   *          A null value or the value x-unspecified for this argument only
+   *            matches ResultSpecifications having x-unspecified as their type.
+   *          A language value that is contained within a language in the ResultSpecification
+   *            is considered to match.  In particular:
+   *               Language          ResultSpecification      Result
+   *               x-unspecified     x-unspecified             match
+   *               x-unspecified     en                        no match
+   *               en                x-unspecified             match
+   *               en                en-us                     no match
+   *               en-us             en                        match
    * 
    * @return true if and only if this <code>ResultSpecification</code> contains the feature with
-   *         name <code>aFullFeatureName</code> for the specified language.
+   *         name <code>aFullFeatureName</code> for a matching language.
    */
   public boolean containsFeature(String aFullFeatureName, String aLanguage);
 

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java?rev=981642&r1=981641&r2=981642&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/PrimitiveAnalysisEngine_impl.java Mon Aug  2 17:51:33 2010
@@ -19,9 +19,7 @@
 
 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;
@@ -35,7 +33,6 @@ import org.apache.uima.analysis_engine.A
 import org.apache.uima.analysis_engine.CasIterator;
 import org.apache.uima.analysis_engine.ResultNotSupportedException;
 import org.apache.uima.analysis_engine.ResultSpecification;
-import org.apache.uima.analysis_engine.TypeOrFeature;
 import org.apache.uima.analysis_engine.impl.compatibility.AnalysisComponentAdapterFactory;
 import org.apache.uima.cas.AbstractCas;
 import org.apache.uima.cas.CAS;
@@ -48,7 +45,6 @@ import org.apache.uima.resource.Resource
 import org.apache.uima.resource.ResourceCreationSpecifier;
 import org.apache.uima.resource.ResourceInitializationException;
 import org.apache.uima.resource.ResourceSpecifier;
-import org.apache.uima.resource.metadata.Capability;
 import org.apache.uima.resource.metadata.ProcessingResourceMetaData;
 import org.apache.uima.resource.metadata.ResourceMetaData;
 import org.apache.uima.util.Level;
@@ -66,11 +62,12 @@ public class PrimitiveAnalysisEngine_imp
   
   private static final Class<PrimitiveAnalysisEngine_impl> 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;
+  /**
+   * result specification derived from the output capabilities of this primitive, used in intersection of languages
+   * Recomputed when type system changes
+   */
+  private ResultSpecification rsFromOutputCapabilities;
 
   private boolean mResultSpecChanged;
 
@@ -359,13 +356,17 @@ public class PrimitiveAnalysisEngine_imp
         // 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.setTypeSystem(mLastTypeSystem);
+          if (mLastTypeSystem != view.getTypeSystem()) {
+            mLastTypeSystem = view.getTypeSystem();
+            mCurrentResultSpecification.setTypeSystem(mLastTypeSystem);
+            rsFromOutputCapabilities = new ResultSpecification_impl();
+            rsFromOutputCapabilities.addCapabilities(this.getAnalysisEngineMetaData().getCapabilities());
+          }
           // 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());
+          ResultSpecification analysisComponentResultSpec = 
+            ResultSpecification_impl.intersect(mCurrentResultSpecification, (ResultSpecification_impl) rsFromOutputCapabilities);
           mAnalysisComponent.setResultSpecification(analysisComponentResultSpec);
           mResultSpecChanged = false;
         }
@@ -409,52 +410,59 @@ public class PrimitiveAnalysisEngine_imp
     }
   }
 
-  /**
-   * Creates the ResultSpecification to be passed to the AnalysisComponent. This is derived from the
-   * ResultSpec that is input to this AE (via its setResultSpecification method) by intersecting
-   * with the declared outputs of this AE, so that we never ask an AnalysisComponent to produce a
-   * result type that it does not declare in its outputs.
-   * 
-   * @param currentResultSpecification
-   *          the result spec passed to this AE's setResultSpecification method
-   * @param capabilities
-   *          the capabilities of this AE
-   * 
-   * @return a ResultSpecifciation to pass to the AnalysisComponent
-   */
-  protected ResultSpecification computeAnalysisComponentResultSpec(
-          ResultSpecification inputResultSpec, Capability[] capabilities) {
-    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 (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;    
-  }
+//  /**
+//   * Creates the ResultSpecification to be passed to the AnalysisComponent. This is derived from the
+//   * ResultSpec that is input to this AE (via its setResultSpecification method) by intersecting
+//   * with the declared outputs of this AE, so that we never ask an AnalysisComponent to produce a
+//   * result type that it does not declare in its outputs.
+//   * 
+//   * For each type or feature, the intersection includes intersecting the languages:
+//   *   if either has x-unspecified, then the intersection is the languages of the other side.
+//   *   else do a bit-intersection of the languages (this will produce too few results)
+//   *     and then iterate over the smaller of the two sources:
+//   *       for each non-base lang, if not in the other source already, see if the base lang
+//   *       is in the other source, and if so, and the non-base lang.
+//   * 
+//   * @param currentResultSpecification
+//   *          the result spec passed to this AE's setResultSpecification method
+//   * @param capabilities
+//   *          the capabilities of this AE
+//   * 
+//   * @return a ResultSpecifciation to pass to the AnalysisComponent
+//   */
+//  protected ResultSpecification computeAnalysisComponentResultSpec(
+//          ResultSpecification inputResultSpec, Capability[] capabilities) {
+//    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 (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;    
+//  }
     
 //    for (int i = 0; i < capabilities.length; i++) {
 //      Capability cap = capabilities[i];

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java?rev=981642&r1=981641&r2=981642&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/ResultSpecification_impl.java Mon Aug  2 17:51:33 2010
@@ -184,7 +184,7 @@ public final class ResultSpecification_i
   /**
    * Default language set to use if nothing else is specified
    */
-  private static final String[] mDefaultLanguage = new String[] {Language.UNSPECIFIED_LANGUAGE};
+  private static final String[] UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1 = new String[] {Language.UNSPECIFIED_LANGUAGE};
 
   /**
    * The type system used to compute the subtypes and allAnnotatorFeatures of types
@@ -252,7 +252,7 @@ public final class ResultSpecification_i
     }
   }
   
-  private String getBaseLanguage(String language) {
+  private static String getBaseLanguage(String language) {
     String baseLanguage = language;
     int index = language.indexOf(LANGUAGE_SEPARATOR);
     if (index > -1) {
@@ -281,6 +281,15 @@ public final class ResultSpecification_i
   }
 
   /**
+   * return the set of languages for this type or feature, or null if no such type/feature
+   */
+  private ToF_Languages getLanguagesForTypeOrFeature(String typeOrFeature) {
+    boolean isType = typeOrFeature.indexOf(TypeSystem.FEATURE_SEPARATOR) == -1;
+    Map<String, ToF_Languages> tofMap = (isType) ? availName2tof_langs() : name2tof_langs;
+    return tofMap.get(typeOrFeature);
+  }
+  
+  /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#getResultTypesAndFeatures(java.lang.String)
    */
   public TypeOrFeature[] getResultTypesAndFeatures(String language) {
@@ -324,11 +333,11 @@ public final class ResultSpecification_i
    */
   private String [] normalizeLanguages(String [] languages) {   
     if (null == languages) {
-      return mDefaultLanguage;
+      return UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1;
     } else {
       for (String lang : languages) {
         if (lang.equals(Language.UNSPECIFIED_LANGUAGE)) {
-          return mDefaultLanguage;
+          return UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1;
         }
       }
     }
@@ -369,6 +378,12 @@ public final class ResultSpecification_i
     }
     setNeedsCompilation();
   }
+  
+  private void addClonedToF_Languages(ToF_Languages tofLangs) {
+    ToF_Languages cloned = (ToF_Languages) tofLangs.clone();
+    name2tof_langs.put(cloned.tof.getName(), cloned);
+    setNeedsCompilation();
+  }
 
   private TypeOrFeature createTypeOrFeature(String name, boolean isType, boolean aAllAnnotatorFeatures) {
     TypeOrFeature r = new TypeOrFeature_impl();
@@ -423,7 +438,7 @@ public final class ResultSpecification_i
     BitSet langBitSet = tof_langs.languages;
     
     // "==" ok here due to normalizeLanguages call above
-    if (languages == mDefaultLanguage) {
+    if (languages == UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1) {
       if ( ! langBitSet.get(UNSPECIFIED_LANGUAGE_INDEX)) {
         langBitSet.clear();
         langBitSet.set(UNSPECIFIED_LANGUAGE_INDEX);
@@ -471,7 +486,7 @@ public final class ResultSpecification_i
    * @see org.apache.uima.analysis_engine.ResultSpecification#setResultTypesAndFeatures(org.apache.uima.analysis_engine.TypeOrFeature[])
    */
   public void setResultTypesAndFeatures(TypeOrFeature[] aTypesAndFeatures) {
-    setResultTypesAndFeatures(aTypesAndFeatures, mDefaultLanguage);
+    setResultTypesAndFeatures(aTypesAndFeatures, UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1);
   }
   
   /**
@@ -490,7 +505,7 @@ public final class ResultSpecification_i
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature)
    */
   public void addResultTypeOrFeature(TypeOrFeature aTypeOrFeature) {
-    addTypeOrFeatureInternal(aTypeOrFeature, mDefaultLanguage);
+    addTypeOrFeatureInternal(aTypeOrFeature, UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1);
   }
 
   /**
@@ -506,7 +521,7 @@ public final class ResultSpecification_i
    *      boolean)
    */
   public void addResultType(String aTypeName, boolean aAllAnnotatorFeatures) {
-    addTypeOrFeatureInternal(createTypeOrFeature(aTypeName, true, aAllAnnotatorFeatures), mDefaultLanguage);
+    addTypeOrFeatureInternal(createTypeOrFeature(aTypeName, true, aAllAnnotatorFeatures), UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1);
   }
   
   /**
@@ -521,7 +536,7 @@ public final class ResultSpecification_i
    * @see org.apache.uima.analysis_engine.ResultSpecification#addResultFeature(java.lang.String)
    */
   public void addResultFeature(String aFullFeatureName) {
-    addResultFeature(aFullFeatureName, mDefaultLanguage);
+    addResultFeature(aFullFeatureName, UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1);
   }
 
   /**
@@ -654,7 +669,7 @@ public final class ResultSpecification_i
     compileIfNeeded();
     return languageMatches(availName2tof_langs().get(aTypeName), language);
   }
-
+  
   /**
    * @see org.apache.uima.analysis_engine.ResultSpecification#containsFeature(java.lang.String)
    */
@@ -750,7 +765,6 @@ public final class ResultSpecification_i
            languages.get(baseLanguageIndex);
   }
   
-
   /**
    * @see org.apache.uima.resource.impl.MetaDataObject_impl#getXmlizationInfo()
    */
@@ -780,7 +794,7 @@ public final class ResultSpecification_i
       String[] supportedLanguages = capability.getLanguagesSupported();
       if (null == supportedLanguages ||
           supportedLanguages.length == 0) {
-        supportedLanguages = mDefaultLanguage;
+        supportedLanguages = UNSPECIFIED_LANGUAGE_IN_ARRAY_OF_1;
       }
       for (TypeOrFeature tof : tofs) {
         addResultTypeOrFeatureAddLanguage(tof, supportedLanguages);
@@ -835,5 +849,119 @@ public final class ResultSpecification_i
     sb.append("mTypeSystem = ").append(mTypeSystem).append("\n");
     return sb.toString();
   }
+  
+  /**
+   * Compute the feature/type + language intersection of two result specs
+   * Result-spec 2 is the more-or-less constant spec from the primitive's capability outputs
+   *   it can change if the type system changes... causing new 'inheritance"
+   *   
+   * Language intersection is done on a per-type-or-feature basis:
+   *   Each is a set of languages, interpreted as a "Union".
+   *     If the set contains x-unspecified - it is taken to mean all languages
+   *     if the set contains XX - it is taken to mean the union of all sublanguages XX-yy
+   *     
+   * package scope
+   */
+
+  static ResultSpecification_impl intersect(ResultSpecification rs1in, ResultSpecification_impl rs2in) {
+    ResultSpecification_impl rs1 = (ResultSpecification_impl) rs1in;
+    ResultSpecification_impl rs2 = (ResultSpecification_impl) rs2in;
+    ResultSpecification_impl newRs = new ResultSpecification_impl(rs1.getTypeSystem());
+    
+    rs1.compileIfNeeded();  // compile to make the next tests for type intersecting work
+    rs2.compileIfNeeded();
+    
+    // iterate over all types and features in this component's result set
+    for (Map.Entry<String, ToF_Languages> item : rs2.availName2tof_langs().entrySet()) {
+      String rs2tof = item.getKey();
+      ToF_Languages rs2Langs = item.getValue();
+      // see if in other resultSpec
+      ToF_Languages rs1Langs = rs1.getLanguagesForTypeOrFeature(rs2tof);
+      if (rs1Langs == null) {
+        continue;
+      }
+
+      // Type or Feature is in both; intersect the languages
+      // if either has language x-unspecified, use the other's language spec.
+      if (rs1Langs.languages.get(ResultSpecification_impl.UNSPECIFIED_LANGUAGE_INDEX)) {
+        newRs.addClonedToF_Languages(rs2Langs);
+        continue;
+      }
+      if (rs2Langs.languages.get(ResultSpecification_impl.UNSPECIFIED_LANGUAGE_INDEX)) {
+        newRs.addClonedToF_Languages(rs1Langs);
+        continue;
+      }
+
+      // Intersect languages - neither has x-unspecified
 
+      List<String> rsltLangs = computeResultLangIntersection(rs1, rs1Langs, rs2, rs2Langs);
+ 
+      if (rsltLangs.size() > 0) {
+        newRs.addResultTypeOrFeature(rs2Langs.tof, rsltLangs.toArray(new String[rsltLangs.size()]));
+      }
+    }
+    return newRs;
+  }
+  
+  private static List<String> computeResultLangIntersection(
+      ResultSpecification_impl rs1, ToF_Languages rs1Langs,     
+      ResultSpecification_impl rs2, ToF_Languages rs2Langs) {
+
+    BitSet rs1bs = rs1Langs.languages;
+    BitSet rs2bs = rs2Langs.languages;
+    List<String> rsltLangs = new ArrayList<String>();
+
+    // because we don't have a list of languages as "Strings",
+    // iterate over all the languages, and skip those not in this
+    // type-or-feature
+    for (Map.Entry<String, Integer> langIndex2 : rs2.lang2int.entrySet()) {
+      if (!rs2bs.get(langIndex2.getValue())) {
+        continue;
+      }
+
+      // String intersectLang = intersectLanguages(langIndex.getKey(),
+      // rs1Langs, rs2Langs);
+
+      String thisLang = langIndex2.getKey();
+      if (rs1bs.get(rs1.getLanguageIndex(thisLang))) {
+        rsltLangs.add(thisLang);
+        continue;
+      }
+
+      // thisLang is not in the set of rs1 languages, but it might still be
+      // in the intersection, if thisLang is not a base form, and the base
+      // form
+      // *is* in the set of rs1 languages
+      String baseLang = getBaseLanguage(thisLang);
+      if (baseLang != thisLang) { // thisLang is not a base form
+        if (rs1bs.get(rs1.getLanguageIndex(baseLang))) {
+          rsltLangs.add(thisLang);
+          continue;
+        }
+      }
+    }
+    
+    // add in more specific langs in rs1 matching general lang in rs2
+   
+    // because we don't have a list of languages as "Strings",
+    // iterate over all the languages, and skip those not in this
+    // type-or-feature
+    for (Map.Entry<String, Integer> langIndex1 : rs1.lang2int.entrySet()) {
+      if (!rs1bs.get(langIndex1.getValue())) {
+        continue;
+      }
+
+      String rsLang1 = langIndex1.getKey();
+      if (rs2bs.get(rs2.getLanguageIndex(rsLang1))) {
+        continue;  // skip this if already would be in intersection
+      }
+      String baseLang1 = getBaseLanguage(rsLang1);
+      if (rsLang1 != baseLang1) {  // rsLang1 is not a base form
+        if (rs2bs.get(rs2.getLanguageIndex(baseLang1))) {
+          rsltLangs.add(rsLang1);  // add specific lang to intersection
+        }
+      }
+    }
+    return rsltLangs;
+  }
 }

Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java?rev=981642&r1=981641&r2=981642&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/AnalysisEngine_implTest.java Mon Aug  2 17:51:33 2010
@@ -1331,27 +1331,7 @@ public class AnalysisEngine_implTest ext
     }
   }
 
-  public void testComputeAnalysisComponentResultSpec() throws Exception {
-    try {
-      AnalysisEngineDescription aeDesc = UIMAFramework.getXMLParser()
-              .parseAnalysisEngineDescription(
-                      new XMLInputSource(JUnitExtension.getFile("SequencerTest/Annotator1.xml")));
-      PrimitiveAnalysisEngine_impl ae = (PrimitiveAnalysisEngine_impl) UIMAFramework
-              .produceAnalysisEngine(aeDesc);
-      CAS cas = ae.newCAS();
-      ResultSpecification resultSpec = new ResultSpecification_impl();
-      resultSpec.addResultType("uima.tt.TokenLikeAnnotation", true);
-      resultSpec.setTypeSystem(cas.getTypeSystem());
-      ResultSpecification acResultSpec = ae.computeAnalysisComponentResultSpec(resultSpec, ae
-              .getAnalysisEngineMetaData().getCapabilities());
-      assertTrue(acResultSpec.containsType("uima.tt.TokenAnnotation"));
-      assertFalse(acResultSpec.containsType("uima.tt.SentenceAnnotation"));
-      assertFalse(acResultSpec.containsType("uima.tt.Lemma"));
-    } catch (Exception e) {
-      JUnitExtension.handleException(e);
-    }
-  }
-  
+
   public void testProcessWithError() throws Exception {
     try {
       //This test uses an aggregate AE fails if the document text is set to "ERROR".

Added: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/ResultSpecTest.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/ResultSpecTest.java?rev=981642&view=auto
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/ResultSpecTest.java (added)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/analysis_engine/impl/ResultSpecTest.java Mon Aug  2 17:51:33 2010
@@ -0,0 +1,165 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.uima.UIMAException;
+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.ResultSpecification;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.test.junit_extension.JUnitExtension;
+import org.apache.uima.util.XMLInputSource;
+
+public class ResultSpecTest extends TestCase {
+
+  private CAS cas;
+  
+  private static final String EN = "en";
+  private static final String X  = "x-unspecified";
+  private static final String EN_US = "en-us";
+  private static final String I = "I";  // split designator
+  
+  /**
+   * Tests for https://issues.apache.org/jira/browse/UIMA-1840
+   */
+  public void testIntersection() {
+    checkl(X, I, X, I, X);
+    checkl(X, I, EN, I,            EN);
+    checkl(EN, I,            X, I, EN);
+    checkl(X, I, EN_US, I,        EN_US);
+    checkl(EN_US, I,       X, I, EN_US);
+    
+    checkl(EN, I, EN, I, EN);
+    checkl(EN, I, EN_US, I, EN_US);
+    checkl(EN_US, I, EN, I, EN_US);
+    checkl(EN_US, I, EN_US, I, EN_US);
+    
+    checkl(X, EN, I, EN, X, I, X);
+    checkl(X, EN, I, EN, I, EN);
+    checkl(EN, EN_US, I, X, I, EN, EN_US);
+  }
+  
+  private void checkl(String... args) {
+    List<String> rs1List = new ArrayList<String>();
+    List<String> rs2List = new ArrayList<String>();
+    List<String> expList = new ArrayList<String>();
+    List<String>[] tgts = new List[]{rs1List, rs2List, expList};
+    int tgtI = 0;
+    
+    for (int i = 0; i < args.length; i++) { 
+      if (args[i] == I) {
+        tgtI ++;
+      } else {
+        tgts[tgtI].add(args[i]);            
+      }
+    }
+    String[] rs1langs = rs1List.toArray(new String[rs1List.size()]);
+    String[] rs2langs = rs2List.toArray(new String[rs2List.size()]);
+    String[] explangs = expList.toArray(new String[expList.size()]);
+
+    ResultSpecification_impl rs1 = new ResultSpecification_impl();
+    ResultSpecification_impl rs2 = new ResultSpecification_impl();
+    ResultSpecification_impl rsE = new ResultSpecification_impl(); //expected
+    
+    rs1.addResultType("T1", true, rs1langs);
+    rs2.addResultType("T1", true, rs2langs);
+    rsE.addResultType("T1", true, explangs);
+    
+    ResultSpecification_impl rsQ = ResultSpecification_impl.intersect(rs1, rs2);
+    assertTrue(rsQ.equals(rsE));
+  }
+  
+  public void testComputeAnalysisComponentResultSpec() throws Exception {
+    try {
+      AnalysisEngineDescription aeDesc = UIMAFramework.getXMLParser()
+              .parseAnalysisEngineDescription(
+                      new XMLInputSource(JUnitExtension.getFile("SequencerTest/Annotator1.xml")));
+      PrimitiveAnalysisEngine_impl ae = (PrimitiveAnalysisEngine_impl) UIMAFramework
+              .produceAnalysisEngine(aeDesc);
+      CAS cas = ae.newCAS();
+      ResultSpecification resultSpec = new ResultSpecification_impl();
+      resultSpec.addResultType("uima.tt.TokenLikeAnnotation", true);
+      resultSpec.setTypeSystem(cas.getTypeSystem());
+      
+      ResultSpecification_impl rs2 = new ResultSpecification_impl(cas.getTypeSystem());
+      rs2.addCapabilities(ae.getAnalysisEngineMetaData().getCapabilities());
+      ResultSpecification acResultSpec = ResultSpecification_impl.intersect(resultSpec, rs2);
+      assertTrue(acResultSpec.containsType("uima.tt.TokenAnnotation"));
+      assertFalse(acResultSpec.containsType("uima.tt.SentenceAnnotation"));
+      assertFalse(acResultSpec.containsType("uima.tt.Lemma"));
+    } catch (Exception e) {
+      JUnitExtension.handleException(e);
+    }
+  }
+  
+  public void testComputeAnalysisComponentResultSpecInherit() throws Exception {
+    try {
+      AnalysisEngineDescription aeDesc = UIMAFramework.getXMLParser()
+              .parseAnalysisEngineDescription(
+                      new XMLInputSource(JUnitExtension.getFile("SequencerTest/Annotator1.xml")));
+      PrimitiveAnalysisEngine_impl ae = (PrimitiveAnalysisEngine_impl) UIMAFramework
+              .produceAnalysisEngine(aeDesc);
+      CAS cas = ae.newCAS();
+      ResultSpecification resultSpec = new ResultSpecification_impl(cas.getTypeSystem());
+      resultSpec.addResultType("uima.tcas.Annotation", true);
+      
+      ResultSpecification_impl rs2 = new ResultSpecification_impl(cas.getTypeSystem());
+      rs2.addCapabilities(ae.getAnalysisEngineMetaData().getCapabilities());
+      ResultSpecification acResultSpec = ResultSpecification_impl.intersect(resultSpec, rs2);
+      assertTrue(acResultSpec.containsType("uima.tt.TokenAnnotation"));
+      assertTrue(acResultSpec.containsType("uima.tt.SentenceAnnotation"));
+      assertFalse(acResultSpec.containsType("uima.tt.Lemma"));
+    } catch (Exception e) {
+      JUnitExtension.handleException(e);
+    }
+  }
+  
+  private ResultSpecification createResultSpec(String language) {
+    ResultSpecification resultSpec = new ResultSpecification_impl(cas.getTypeSystem());
+    resultSpec.addResultType("Type1", true, new String[] {language});
+    return resultSpec;
+  }
+    
+  /**
+   * Auxiliary method used by testProcess()
+   * 
+   * @param aTaeDesc
+   *          description of TextAnalysisEngine to test
+   */
+  protected void _testProcess(AnalysisEngineDescription aed, String language) throws UIMAException {
+    // create and initialize AnalysisEngine
+    AnalysisEngine ae = UIMAFramework.produceAnalysisEngine(aed);
+    cas = ae.newCAS();
+
+    ResultSpecification resultSpec = createResultSpec(language);
+    
+    cas.setDocumentText("new test");
+    ae.process(cas, resultSpec);
+    cas.reset();
+    ae.destroy();
+  }
+
+}