/*
* 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.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.impl.TypeSystemImpl;
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;
import org.apache.uima.resource.metadata.impl.XmlizationInfo;
/**
* Reference implementation of {@link ResultSpecification}.
*
* Notes on the implementation
*
* Result Specifications (result specs, rs) are closely tied to capability specifications.
*
* They consist of instances of
* TypeOrFeatures and associated languages for which they are set.
*
* This impl supports removing previously added types and features
* for particular languages.
*
* There are two forms of the data kept:
* The data as it was provided to set the items in the result spec
* This form is used when removing previously added things
*
* The data after a type system has been provided, expanded to cover
* the various implied settings, due to either
* all Features flag on a type or
* the type/subtype hierarchy in the type system
*
* TypesOrFeatures are:
* typeXXX:FeatureYYY - specifying a particular feature of a type
* (Corner case: typeXXX:FeatureYYY doesn't imply there's a
* typeXXX allFeat nor a
* typeXXX w/o allFeat.
*
* typeXXX with allFeatures - a shorthand for specifying
* typeXXX and
* typeXXX:FeatureYYY for all features YYY defined for typeXXX
* (Corner case: excludes features ZZZ defined only in subtype of typeXXX)
* typeXXX without allFeatures (w/o allFeat) - specifies a type, but says nothing about the features
* This is specifiable in the XML. It means:
* The type is produced/needed but there's no information about the features that
* are to be produced or used
*
* containsType typeXXX
* returns true if typeXXX is in the RS, with or without the allFeats flag
* returns false if only features involving typeXXX are specified
*
* Intersection is done on fully expanded representations.
*
* There are two kinds of inheritance used
* Assuming there's a type system (which must be present when intersection is used), there's type/subtype
* This means that if a resultSpec is set for typeXXX, then the containsType(typeYYY)
* returns true if typeYYY is a subtype of typeXXX.
* This also needs to work for typeXXX:featZZZ; containsFeature(typeYYY:featZZZ)
* returns true if type YYY is a subtype of typeXXX.
*
* Languages have a 3 level hierarchy:
* x-unspecified - the same as no language being specified.
* If the resultSpec contains typeXXX for language x-unspecified,
* containsType(typeXXX, languageLLL) returns true, for any languageLLL
* a "base" language, without a '-', e.g. "en"
* a sub-language, with one or more '-', e.g., "en-us"
*
* The rules for matching languages only handle these three levels of inheritance.
* (Corner case: 3 or more level language hierarchy are treated as 3 level hierarchies
* eg. zh-Hant-HK (Traditional Chinese as used in Hong Kong)
* See http://www.w3.org/International/articles/language-tags/Overview.en.php )
*
* Design considerations and assumptions
* Many instances of this class are made, sometimes via cloning.
* Most uses only use types, not type:features
* Most don't use languages
* A small subset of the possible types and type:features is specified explicitly
* Sometimes types and/or features are deleted. (language capability flow deletes types and/or features)
*
* Types and Features are kept on a per-language basis. Language can include a special value,
* x-unspecified, which "matches" any other language.
*
* Set operations among different result specs:
* Union: done in aggregates over result-specs derived from input capabilities of delegates
* Intersection: done for primitive components, over result-spec derived from output capability of the primitive
* remove: one type or feature (used by language capability flow)
* (Corner cases
* removing typeXXX doesn't remove typeXXX:featureYYY
* removing typeXXX allFeat doesn't remove typeXXX w/o allFeat (may have different languages)
* removing typeXXX w/o allFeat doesn't remove typeXXX allFeat
*
* The compiled version is used in containsType, containsFeature testing, and is used when
* computing intersection.
*/
public final class ResultSpecification_impl extends MetaDataObject_impl implements
ResultSpecification {
private static final long serialVersionUID = 8516517600467270594L;
/**
* main language separator e.g 'en' and 'en-US'
*/
private static final String[] ARRAY_X_UNSPEC = new String[]{Language.UNSPECIFIED_LANGUAGE};
/**
* form used in hash table of compilied version to represent x-unspecified
* (can't use null - that means entry not in table)
*/
private static final RsLangs compiledXunspecified = RsLangs.createSharableEmpty(); // a distinct object
/**
* used for empty type subsumption lists in subtype iterator
*/
public static final List<Type> EMPTY_TYPE_LIST = new ArrayList<Type>(0);
/**
* For this Result-specification, the collection of language-sets
* Uncompiled format
*/
private final RsTypesMap rsTypesMap;
/**
* The type system used to compute the subtypes and allAnnotatorFeatures of types
*/
private TypeSystem mTypeSystem = null;
// compiled forms
private boolean needsCompilation = true;
private final Map<String, RsLangs> rsCompiled;
public ResultSpecification_impl() {
rsTypesMap = new RsTypesMap();
rsCompiled = new HashMap<String, RsLangs>();
}
/**
* 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;
}
/**
* copies the result spec passed in so that updates to it
* don't affect the original
* @param original
*/
private ResultSpecification_impl(ResultSpecification_impl original) {
mTypeSystem = original.mTypeSystem; // not cloned
rsTypesMap = new RsTypesMap(original.rsTypesMap);
needsCompilation = original.needsCompilation;
rsCompiled = new HashMap<String, RsLangs>(original.rsCompiled);
for (Map.Entry<String, RsLangs> e : rsCompiled.entrySet()) {
e.getValue().setShared();
}
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#getResultTypesAndFeatures()
*/
public TypeOrFeature[] getResultTypesAndFeatures() {
return getResultTypesAndFeatures(true, null);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#getResultTypesAndFeatures(java.lang.String)
* May contain near-duplicates - same type, but with different settings of allannotatorfeatures
* (only if they have different languages)
*/
public TypeOrFeature[] getResultTypesAndFeatures(String language) {
return getResultTypesAndFeatures(false, language);
}
private TypeOrFeature[] getResultTypesAndFeatures(boolean skipLanguageFilter, String language) {
List<TypeOrFeature> r = new ArrayList<TypeOrFeature>();
if (rsTypesMap.nbrOfTypes() == 0 && needsCompilation == false) {
// being called on results of intersection
// probably by a test case, not a normal call
// attempt to construct a plausible representation
reconstructRsTypesFromCompiled();
}
for (RsType t : rsTypesMap) {
if (t.isAllFeatures && (skipLanguageFilter || RsLangs.subsumes(t.languagesAllFeat, language))) {
r.add(createTypeOrFeature(t.typeName, true, true));
}
if (t.isSpecified && (skipLanguageFilter || RsLangs.subsumes(t.languagesNotAllFeat, language))) {
if (!(t.isAllFeatures && t.languagesAllFeat.equals(t.languagesNotAllFeat))) // don't make a duplicate
r.add(createTypeOrFeature(t.typeName, true, false));
}
if (t.features != null) {
for (RsFeat f : t.features) {
if (skipLanguageFilter || f.subsumes(language))
r.add(createTypeOrFeature(t.typeName, f.shortFeatName));
}
}
}
return r.toArray(new TypeOrFeature[r.size()]);
}
private void reconstructRsTypesFromCompiled() {
// First, recompute basic rsTypes and rsFeatures hooked to types
for (Entry<String, RsLangs> e : rsCompiled.entrySet()) {
String tofName = e.getKey();
int b = tofName.indexOf(TypeSystem.FEATURE_SEPARATOR);
if (b == -1) {
rsTypesMap.add(tofName, false, e.getValue(), false);
} else {
String typeName = tofName.substring(0, b);
String featName = tofName.substring(b+1);
rsTypesMap.add(typeName, featName, e.getValue(), false);
}
}
// Second merge
// if the types features all have the same lang and are all the features,
// set the allFeats flag, and merge in the langs
for (RsType t : rsTypesMap) {
if (t.hasAllFeaturesExplicitly(mTypeSystem) && t.allFeaturesHaveSameLangs()) {
t.isAllFeatures = true;
RsLangs l = t.features.features.get(0).languages;
if (l != null && RsLangs.isEmpty(l)) {
l = null;
}
if (l != null) {
if (t.languagesAllFeat == null) {
t.languagesAllFeat = RsLangs.createOrNull(l);
} else { // merge in langs l
t.languagesAllFeat = RsLangs.addAll(t.languagesAllFeat, l);
}
}
t.features = null;
}
if (t.isSpecified && t.isAllFeatures && equalsOrBothNull(t.languagesAllFeat, t.languagesNotAllFeat)) {
t.isSpecified = false;
t.languagesNotAllFeat = null;
}
}
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#setResultTypesAndFeatures(org.apache.uima.analysis_engine.TypeOrFeature[])
*/
public void setResultTypesAndFeatures(TypeOrFeature[] aTypesAndFeatures) {
setResultTypesAndFeatures(aTypesAndFeatures, ARRAY_X_UNSPEC);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#setResultTypesAndFeatures(org.apache.uima.analysis_engine.TypeOrFeature[],
* java.lang.String[])
*/
public void setResultTypesAndFeatures(TypeOrFeature[] aTypesAndFeatures, String[] aLanguageIDs) {
for (TypeOrFeature tof : aTypesAndFeatures) {
addResultTof(tof, aLanguageIDs, true);
}
}
private void addResultTof(TypeOrFeature tof, String[] langs, boolean replace) {
String name = tof.getName();
String typeName = null;
String shortFeatName = null;
int i = name.indexOf(TypeSystem.FEATURE_SEPARATOR);
if (i < 0) {
typeName = name;
rsTypesMap.add(typeName, tof.isAllAnnotatorFeatures(), langs, replace);
} else {
typeName = name.substring(0, i);
shortFeatName = name.substring(i+1);
rsTypesMap.add(typeName, shortFeatName, langs, replace);
}
setCompileNeeded();
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature)
*/
public void addResultTypeOrFeature(TypeOrFeature aTypeOrFeature) {
addResultTypeOrFeature(aTypeOrFeature, ARRAY_X_UNSPEC);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature,
* java.lang.String[])
*
* Note: Javadoc makes assumption that there's one tof per type, but this design allows 2 (one with allAnnotatorFeatures set or not).
*/
public void addResultTypeOrFeature(TypeOrFeature tof, String[] languages) {
addResultTof(tof, languages, true);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultType(java.lang.String,
* boolean)
*/
public void addResultType(String aTypeName, boolean aAllAnnotatorFeatures) {
addResultType(aTypeName, aAllAnnotatorFeatures, ARRAY_X_UNSPEC);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultType(java.lang.String,
* boolean, java.lang.String[])
*/
public void addResultType(String aTypeName, boolean aAllAnnotatorFeatures, String[] aLanguageIDs) {
rsTypesMap.add(aTypeName, aAllAnnotatorFeatures, aLanguageIDs, false);
setCompileNeeded();
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultFeature(java.lang.String)
*/
public void addResultFeature(String aFullFeatureName) {
addResultFeature(aFullFeatureName, ARRAY_X_UNSPEC);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addResultFeature(java.lang.String,
* java.lang.String[])
*/
public void addResultFeature(String aFullFeatureName, String[] aLanguageIDs) {
String typeName = null;
String shortFeatName = null;
int i = aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR);
typeName = aFullFeatureName.substring(0, i);
shortFeatName = aFullFeatureName.substring(i+1);
rsTypesMap.add(typeName, shortFeatName, aLanguageIDs, false);
setCompileNeeded();
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#compile(org.apache.uima.cas.TypeSystem)
* @deprecated no longer needed, remove call to this
*/
@Deprecated
public void compile(TypeSystem aTypeSystem) {
setTypeSystem(aTypeSystem);
compile();
}
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 TypeOrFeature createTypeOrFeature(String typeName, String featureName) {
return createTypeOrFeature(typeName + TypeSystem.FEATURE_SEPARATOR + featureName, false, false);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#containsType(java.lang.String)
*/
public boolean containsType(String aTypeName) {
return containsType(aTypeName, Language.UNSPECIFIED_LANGUAGE);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#containsType(java.lang.String,java.lang.String)
* method:
*
* for each type (with all-feat, without all-feat):
* for each type, and supertypes
* check if one of the resultSpec languages subsumes the given language.
* if so, return true
* return false;
*
* But: cache this: key = int[2]: type#, langi#, value = true/false
*
*/
// TODO check cache, normalize language
public boolean containsType(String aTypeName, String aLanguage) {
if (aTypeName.indexOf(TypeSystem.FEATURE_SEPARATOR) != -1) {
return false; // check against someone passing a feature name here
}
compileIfNeeded();
return hasLanguage(rsCompiled.get(aTypeName), aLanguage);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#containsFeature(java.lang.String)
*/
public boolean containsFeature(String aFullFeatureName) {
return containsFeature(aFullFeatureName, Language.UNSPECIFIED_LANGUAGE);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#containsFeature(java.lang.String,java.lang.String)
*/
public boolean containsFeature(String aFullFeatureName, String aLanguage) {
int i = aFullFeatureName.indexOf(TypeSystem.FEATURE_SEPARATOR);
if (i == -1)
return false; // check against someone passing a type name here
compileIfNeeded();
boolean found = hasLanguage(rsCompiled.get(aFullFeatureName), aLanguage);
if (found) {
return true;
}
// this next bit is to keep the behavior in the case where the type system isn't specified,
// the same.
RsType t = rsTypesMap.getRsType(aFullFeatureName.substring(0, i)); // look for just the type name
if (null != t && t.isAllFeatures && RsLangs.subsumes(t.languagesAllFeat, aLanguage)) {
return true;
}
return false;
}
/**
*
* @param rsLangs
* @param language
* @return
*/
private static boolean hasLanguage(RsLangs rsLangs, String language) {
language = Language.normalize(language);
// rsLangs == null means there was no entry in the
// rsCompiled map for this type
// It does NOT mean x-unspecified
return (rsLangs == null) ? false : (RsLangs.subsumes(rsLangs, language));
}
/**
* @see org.apache.uima.resource.impl.MetaDataObject_impl#getXmlizationInfo()
*/
@Override
protected XmlizationInfo getXmlizationInfo() {
return new XmlizationInfo("resultSpecification", null,
new PropertyXmlInfo[] { new PropertyXmlInfo("resultTypesAndFeatures", null) });
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addCapabilities(org.apache.uima.resource.metadata.Capability[])
*/
public void addCapabilities(Capability[] capabilities) {
addCapabilities(capabilities, true);
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#addCapabilities(org.apache.uima.resource.metadata.Capability[],
* boolean)
*/
public void addCapabilities(Capability[] capabilities, boolean outputs) {
if (null == capabilities) {
return;
}
for (Capability capability : capabilities) {
TypeOrFeature[] tofs = outputs ? capability.getOutputs() : capability.getInputs();
for (TypeOrFeature tof : tofs) {
String typeName = tof.getName();
if (!tof.isType()) {
int i = typeName.indexOf(TypeSystem.FEATURE_SEPARATOR);
String shortFeatName = typeName.substring(i+1);
typeName = typeName.substring(0, i);
rsTypesMap.add(typeName, shortFeatName, capability.getLanguagesSupported(), false);
} else {
rsTypesMap.add(typeName, tof.isAllAnnotatorFeatures(), capability.getLanguagesSupported(), false);
}
}
}
setCompileNeeded();
}
/**
* @see org.apache.uima.analysis_engine.ResultSpecification#removeTypeOrFeature(org.apache.uima.analysis_engine.TypeOrFeature)
* This removes the type or feature for all languages.
* Beware: there are two possible ToFs one with allFeatures set or not (if they have different languages).
*/
public void removeTypeOrFeature(TypeOrFeature tof) {
String name = tof.getName();
if (tof.isType()) {
rsTypesMap.remove(name);
} else {
int i = name.indexOf(TypeSystem.FEATURE_SEPARATOR);
rsTypesMap.remove(name.substring(0, i), name.substring(i+1));
}
setCompileNeeded();
}
/**
* returns a clone of the <code>ResultSpecification</code> object.
*
* @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.
return new ResultSpecification_impl(this);
}
public void setTypeSystem(TypeSystem ts) {
mTypeSystem = ts;
setCompileNeeded();
}
public TypeSystem getTypeSystem() {
return mTypeSystem;
}
@SuppressWarnings("unchecked")
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("org.apache.uima.analysis_engine.impl.ResultSpecification_impl:\n");
sb.append(" needsCompilation = ").append(needsCompilation).append("\n");
// sb.append("lang2int = ").append(lang2int).append("\n");
// sb.append("name2tof_langs = ").append(name2tof_langs).append("\n");
// sb.append("withSubtypesName2tof_langs = ").append(withSubtypesName2tof_langs).append("\n");
sb.append("\nrsTofLangs:\n");
if (needsCompilation) {
sb.append(rsTypesMap);
} else {
Object [] sorted = rsCompiled.entrySet().toArray();
Arrays.sort(sorted, new Comparator<Object>() {
public int compare(Object object1, Object object2) {
return ((Entry<String, RsLangs>)object1).getKey().
compareTo(((Entry<String, RsLangs>)object2).getKey());
}
});
for (Object o : sorted) {
Entry<String, RsLangs> e = (Entry<String, RsLangs>) o;
String k = e.getKey();
k = k + " ".substring(k.length()%8);
sb.append(" key: ").append(k).append(" value: ").append(e.getValue()).append("\n");
}
}
sb.append("\n\nmTypeSystem = ").append(mTypeSystem).append("\n");
return sb.toString();
}
private void compileIfNeeded() {
if (needsCompilation) {
needsCompilation = false;
compile();
}
}
private void setCompileNeeded() {
needsCompilation = true;
rsCompiled.clear();
}
/**
* create a fully expanded version of this result spec
*/
private void compile() {
for (RsType rst : rsTypesMap) {
if (rst.isSpecified) {
addCompiledFormForTypeAndItsSubtypes(rst, rst.languagesNotAllFeat);
}
if (rst.isAllFeatures) {
addCompiledFormForTypeAndItsSubtypes(rst, rst.languagesAllFeat);
for (Feature f : rst.getAllAppropriateFeatures(mTypeSystem)) {
addCompiledFormForFeatureAndItsSubtypes(rst, f.getShortName(), rst.languagesAllFeat);
}
}
if (rst.features != null) {
for (RsFeat rsf : rst.features) {
addCompiledFormForFeatureAndItsSubtypes(rst, rsf.shortFeatName, rsf.languages);
}
}
}
}
private void addCompiledFormForTypeAndItsSubtypes(RsType rst, RsLangs langs) {
addCompiledFormEntry(rst.typeName, langs);
for (String subtypeName : subtypeNames(rst.typeName)) {
addCompiledFormEntry(subtypeName, langs);
}
}
/**
* Note: the string typeXXX:featYYY may not be in the type system.
* For instance, if featYYY is introduced in type Foo, we could have a spec of
* FooSubtype:featYYY; this string could be unique to the result spec
* @param rst
* @param shortFeatName
* @param langs
*/
private void addCompiledFormForFeatureAndItsSubtypes(RsType rst, String shortFeatName, RsLangs langs) {
addCompiledFormEntry(RsFullFeatNames.getFullFeatName(rst.typeName, shortFeatName), langs);
for (String subtypeName : subtypeNames(rst.typeName)) {
addCompiledFormEntry(RsFullFeatNames.getFullFeatName(subtypeName, shortFeatName), langs);
}
}
/**
* Adds languages to a type or feature
* @param tofName
* @param languagesToAdd
*/
private void addCompiledFormEntry(String tofName, RsLangs languagesToAdd) {
if (languagesToAdd == null) {
languagesToAdd = compiledXunspecified;
}
RsLangs rsLangs = rsCompiled.get(tofName);
if (null == rsLangs) {
if (languagesToAdd != compiledXunspecified) {
languagesToAdd.setShared();
}
rsCompiled.put(tofName, languagesToAdd);
return;
}
RsLangs.addAll(rsLangs, languagesToAdd);
}
private Iterable<String> subtypeNames(final String typeName) {
final TypeSystemImpl ts = (TypeSystemImpl) mTypeSystem;
return new Iterable<String>() {
public Iterator<String> iterator() {
return new Iterator<String>() {
Type t = (null == ts) ? null : ts.getType(typeName);
List<Type> subtypes = (null == ts) ? EMPTY_TYPE_LIST
: (null == t ) ? EMPTY_TYPE_LIST
: ts.getProperlySubsumedTypes(t);
int i = 0;
public boolean hasNext() {
return i < subtypes.size();
}
public String next() {
return subtypes.get(i++).getName();
}
public void remove() {throw new UnsupportedOperationException();}
};
}
};
}
/**
* 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
*/
ResultSpecification_impl intersect(ResultSpecification_impl rsOther) {
ResultSpecification_impl r = new ResultSpecification_impl();
r.setTypeSystem(rsOther.mTypeSystem);
r.compileIfNeeded();
rsOther.compileIfNeeded();
compileIfNeeded();
/**
* Iterate over other
*/
for (Iterator<Entry<String, RsLangs>> it = rsOther.rsCompiled.entrySet().iterator(); it.hasNext();) {
Entry<String, RsLangs> e = it.next();
String tofName = e.getKey();
RsLangs otherRsLangs = e.getValue();
/**
* Get corresponding languages from this side
*/
RsLangs thisRsLangs = rsCompiled.get(tofName);
if (null == thisRsLangs) {
continue; // null does NOT mean x-unspecified, it means tof is not present in compiled map at all
}
/**
* Intersect languages, with subsumption
*/
RsLangs intersectRsLangs = thisRsLangs.intersect(otherRsLangs);
if (intersectRsLangs != null) {
r.addCompiledFormEntry(tofName, intersectRsLangs);
}
}
return r;
}
private boolean compiledFormEquals(ResultSpecification_impl other) {
compileIfNeeded();
other.compileIfNeeded();
return rsCompiled.equals(other.rsCompiled); // compares two maps, returns true if have same entries
}
@Override
public boolean equals(Object aObj) {
if (!(aObj instanceof ResultSpecification_impl)) {
return false;
}
return compiledFormEquals((ResultSpecification_impl)aObj);
}
static boolean equalsOrBothNull(Object x, Object y) {
if (null == x && null == y) {
return true;
}
if (null != x && x.equals(y)) {
return true;
}
return false;
}
@Override
public int hashCode() {
throw new UnsupportedOperationException("HashCode not implemented for ResultSpecification_impl");
}
}