blob: a22735326f7b15bd954ebe9a93d2cc70a3d4f531 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.cas.impl;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import org.apache.uima.cas.CASRuntimeException;
import org.apache.uima.internal.util.Misc;
/**
* This class gets initialized with two type systems, and then provides
* resources to map type and feature codes between them.
*
* It is used by some Binary serialization/ deserialization
* code to allow non-exact matched type systems to send and
* receive CASes in a binary-like format.
*
* Use cases:
*
* Serializing: Source ts -%gt; generate serialized form in Target ts
* Deserializing: Target ts -%gt; generate deserialized form in Source ts
* - either from remote or
* - from disk-stored-form
*
* Mapping details:
* Types are mapped by name.
* Same-named types do not need to have the same number of features.
* Same-named features must have same Range - otherwise, not mapped.
* Types with 0 features mapped allowed.
* LifeCycle:
* Instance of this are created for a CAS when needed, and then
* kept in the (source) TypeSystemImpl, in a map indexed by
* the target type system (identity map)
*
*/
public class CasTypeSystemMapper {
public final TypeSystemImpl tsSrc; // source type system
// weak ref to target type system, to allow that object to be gc'd
// which in turn allows a weak map using these as keys to reclaim space
public final WeakReference<TypeSystemImpl> tsTgt;
/**
* Map from source types to target types.
* Source type code used as index,
* value is target type or null if the type doesn't exist in the target
*/
final private List<TypeImpl> tSrc2Tgt = new ArrayList<>();
/**
* Map from target types to source types.
* Source type code used as index,
* value is target type or null if the type doesn't exist in the target
*/
final private List<TypeImpl> tTgt2Src = new ArrayList<>();
/**
* Feature mapping from source to target
* first key is the src type code, 2nd is the src feature offset
*/
final private FeatureImpl[][] fSrc2Tgt;
/**
* Feature mapping from target to source
* first key is the tgt type code, 2nd is the tgt feature offset
* Only used for type codes that are not arrays.
* Use: When serializing a source type that exists in the target, have to output
* the slots in the target feature order
* Also, when comparing the slots in the target with a given source
*/
final private FeatureImpl[][] fTgt2Src;
final private boolean typeSystemsSame;
public boolean isEqual() {
return this.typeSystemsSame;
}
public CasTypeSystemMapper(TypeSystemImpl tsSrc, TypeSystemImpl tsTgt) {
if (!tsSrc.isCommitted() || !tsTgt.isCommitted()) {
/** Type Systems must be committed before calling this method */
throw new CASRuntimeException(CASRuntimeException.TYPESYSTEMS_NOT_COMMITTED);
}
this.tsSrc = tsSrc;
this.tsTgt = new WeakReference<TypeSystemImpl>(tsTgt);
boolean tss = true;
if (tsSrc != tsTgt) {
fSrc2Tgt = new FeatureImpl[tsSrc.getTypeArraySize()][];
fTgt2Src = new FeatureImpl[tsTgt.getTypeArraySize()][];
boolean b1 = addTypes(tSrc2Tgt, tsSrc, tsTgt);
boolean b2 = addTypes(tTgt2Src, tsTgt, tsSrc); // both directions
boolean b3 = addFeatures(fSrc2Tgt, tsSrc, tsTgt);
boolean b4 = addFeatures(fTgt2Src, tsTgt, tsSrc);
if (!b1 || !b2 || !b3 || !b4) {
tss = false;
}
} else {
fSrc2Tgt = null;
fTgt2Src = null;
}
this.typeSystemsSame = tss;
}
/**
* @param srcType -
* @return Type in other type system, or this one if map is empty
*/
public TypeImpl mapTypeSrc2Tgt(TypeImpl srcType) {
return (tSrc2Tgt.size() == 0) ? srcType : tSrc2Tgt.get(srcType.getCode());
}
// public TypeImpl mapTypeSrc2Tgt(int srcTypeCode) {
// return tSrc2Tgt.get(srcTypeCode);
// }
/**
* @param tgtType -
* @return 0 if type doesn't have corresponding code in other type system
*/
public TypeImpl mapTypeTgt2Src(TypeImpl tgtType) {
return (tTgt2Src.size() == 0) ? tgtType : tTgt2Src.get(tgtType.getCode());
}
public TypeImpl mapTypeCodeTgt2Src(int tgtTypeCode) {
return (tTgt2Src.size() == 0) ? tsSrc.getTypeForCode(tgtTypeCode) : tTgt2Src.get(tgtTypeCode);
}
/**
*
* @param type -
* @param src2tgt -
* @return 0 if type doesn't have corresponding code in other type system
*/
public TypeImpl mapTypeCode2Other(TypeImpl type, boolean src2tgt) {
return (src2tgt) ? mapTypeSrc2Tgt(type) : mapTypeTgt2Src(type);
}
/**
* Get target feature, given src type and feature
* @param srcType the source type
* @param srcFeat the source feature
* @return the target feature or null
*/
public FeatureImpl getTgtFeature(TypeImpl srcType, FeatureImpl srcFeat) {
return getToFeature(fSrc2Tgt, srcType, srcFeat);
}
public FeatureImpl getSrcFeature(TypeImpl tgtType, FeatureImpl tgtFeat) {
return getToFeature(fTgt2Src, tgtType, tgtFeat);
}
/**
* Given a tgt type, return an array of source features in the order
* they would appear in the target.
* @param tgtType -
* @return array of corresponding source features, in target type order
*/
public FeatureImpl[] getSrcFeatures(TypeImpl tgtType) {
return fTgt2Src[tgtType.getCode()];
}
public FeatureImpl getToFeature(FeatureImpl[][] mapByTypeCode, TypeImpl fromType, FeatureImpl fromFeat) {
if (mapByTypeCode == null) { // is null if type systems ==
return fromFeat;
}
FeatureImpl[] map = mapByTypeCode[fromType.getCode()];
if (map == null) {
return null;
}
final int offset = fromFeat.getOffset();
if (map.length <= offset) {
return null;
}
return map[offset];
}
/**
* return true if no types are filtered
* @param map
* @param tsSrc
* @param tsTgt
* @return
*/
private boolean addTypes(List<TypeImpl> map, TypeSystemImpl tsSrc, TypeSystemImpl tsTgt) {
boolean r = true;
for (TypeImpl tSrc : tsSrc.getAllTypes()) {
TypeImpl ti = tsTgt.getType(tSrc.getName());
Misc.setWithExpand(map, tSrc.getCode(), ti);
r = r & (null != ti); // make r true only if all types are found
}
return r;
}
/**
* Create the map from tsFrom to tsTo for all the features, by type
* -- map created using type and feature name equality
* -- note: the features may have different definitions; map is by name only
* --- e.g., one may have String range, the other float range.
* --- in this case, the return is set to false.
* @param map the map to update
* @param tsFrom the From type system
* @param tsTo the to type system
* @return true if all the tsFrom features are found in tsTo and following fields are the same:
* rangeType.name, featureOffset, isMultipleRefsAllowed
*/
private boolean addFeatures(FeatureImpl[][] map, TypeSystemImpl tsFrom, TypeSystemImpl tsTo) {
boolean r = true;
for (TypeImpl ti : tsFrom.getAllTypes()) {
TypeImpl toTi = tsTo.getType(ti.getName());
if (toTi == null) {
r = false;
continue; // no corresponding type in tsTo
}
final FeatureImpl[] map1 = map[ti.getCode()] = new FeatureImpl[ti.getFeatureImpls().length];
for (FeatureImpl fi : ti.getFeatureImpls()) {
FeatureImpl toFi = toTi.getFeatureByBaseName(fi.getShortName());
if (toFi == null) {
r = false;
} else {
map1[fi.getOffset()] = toFi;
// return false if the same-named feature doesn't match
if (r && (!fi.getRange().getName().equals(toFi.getRange().getName()) ||
fi.getOffset() != toFi.getOffset() ||
fi.isMultipleReferencesAllowed() != toFi.isMultipleReferencesAllowed())) {
r = false;
}
}
}
}
return r;
}
}