blob: 007c6218049896db431927a1f468cac8f47cf74c [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.util.AbstractCollection;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.uima.UIMARuntimeException;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.CASException;
import org.apache.uima.cas.CASRuntimeException;
import org.apache.uima.cas.FSComparators;
import org.apache.uima.cas.FSIndex;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.admin.CASAdminException;
import org.apache.uima.cas.admin.FSIndexComparator;
import org.apache.uima.cas.admin.FSIndexRepositoryMgr;
import org.apache.uima.cas.admin.LinearTypeOrder;
import org.apache.uima.cas.admin.LinearTypeOrderBuilder;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.internal.util.IntVector;
import org.apache.uima.internal.util.Misc;
import org.apache.uima.internal.util.ObjHashSet;
import org.apache.uima.jcas.cas.AnnotationBase;
import org.apache.uima.jcas.cas.Sofa;
import org.apache.uima.jcas.cas.TOP;
import org.apache.uima.jcas.tcas.Annotation;
/**
* There is one instance of this class per CAS View.
*
* Some parts of the data here are shared between all views of a CAS.
*
* Many things refer to specific types, and their associated Java Cover classes.
* Java impl classes are always used for each type;
* If there is no JCas cover class defined for a type, then
* the most specific superclass which has a JCas defined class is used;
* this is the class TOP or one of its subclasses.
*
*
* Generic typing:
* User facing APIs can make use of the (JCas) Java cover types, for indexes and iterators over them
* The general generic type used is typically written here as T extends FeatureStructure, where
* FeatureStructure is the super interface of all JCas types.
*
* APIs having no reference to Java cover types (i.e., low level iterators) are not generic, unless they
* are needed to be to pass along the associated type to other APIs.
*/
public class FSIndexRepositoryImpl implements FSIndexRepositoryMgr, LowLevelIndexRepository {
// private final static boolean DEBUG = false;
public final static boolean ITEM_ADDED_TO_INDEX = true;
public final static boolean ITEM_REMOVED_FROM_INDEX = false;
/** set next to true to debug issues with different treatment of no type priorities in v3 */
public final static boolean V2_ANNOTATION_COMPARE_TYPE_ORDER = false;
/**
* The default size of an index.
*/
public static final int DEFAULT_INDEX_SIZE = 16;
/**
* flag used when removing FSs due to corruption avoidance
*/
public static final boolean SKIP_BAG_INDEXES = true;
public static final boolean INCLUDE_BAG_INDEXES = false;
/**
* Define this JVM property to allow adding the same identical FS to Set and Sorted indexes more than once.
*/
public static final String ALLOW_DUP_ADD_TO_INDEXES = "uima.allow_duplicate_add_to_indexes";
static {
if (Misc.getNoValueSystemProperty(ALLOW_DUP_ADD_TO_INDEXES)) {
throw new CASAdminException(CASAdminException.INDEX_DUPLICATES_NOT_SUPPORTED);
}
}
public static final String DISABLE_ENHANCED_WRONG_INDEX = "uima.disable_enhanced_check_wrong_add_to_index";
private static final boolean IS_DISABLE_ENHANCED_WRONG_INDEX_CHECK = // true || // debug
Misc.getNoValueSystemProperty(DISABLE_ENHANCED_WRONG_INDEX);
// Implementation note: the use of equals() here is pretty hairy and
// should probably be fixed. We rely on the fact that when two
// FSIndexComparators are compared, the type of the comparators is
// ignored! A fix for this would be to split the FSIndexComparator
// class into two classes, one for the key-comparator pairs, and one
// for the combination of the two. Note also that we compare two
// FsIndex_iicps by comparing their
// index.getComparator()s.
/**
* The index repository holds instances of defined and built-in indexes.
*
* Indexes implement Java's NavigableSet ( *** do this later ??? may be a bit of work to make work over subtypes )
*
* There are various kinds of iterators that can be obtained:
* Iterator - plain Java iterator - goes forward only
* FSIterator - an extension that can go in either direction, and can locate a position via an FS
*
* Low-level iterators return internal "ints" representing FS ids; these are only for backwards compatibility
* IntPointerIterator - for backwards compatibility - a wrapper around FSIterator, plus inc() dec()
* - may be dropped if only internally used
* LowLevelIterator - for backwards compatibility - a wrapper around FSIterator, plus ll_get, ll_indexSize, ll_getIndex
*
* To obtain normal iterators, use the FSIndex methods
* iterator
* iterator(FeatureStructure) - to initially position to the FS)
* index.withSnapshotIterators() - to get snapshot versions of the indexes to iterate over
*
* To get the low level iterators,
* get a low-level indexrepository (CAS -> getLowLevelCAS -> get low-level index repository), and from there
* get a low-level index
* get a low-level iterator
*/
/**
* General FSIterator creation
*
* There are some alternatives
* If the index has subtypes, then
* - the iterator may be ordered/unordered.
* - Ordered ones select among the subtypes as needed.
* - Unordered ones return all elements from each subtype and then switch to the next subtype.
* This is for efficiency.
* - (currently disabled) flattened indexes may be created and used
* If the index says to use snapshots, then each call makes a snapshot of the index, and iterates over that.
* - these don't throw ConcurrentModificationExceptions.
*/
/*************************************************************
* Information about indexes that is shared across all views *
*************************************************************/
private static class SharedIndexInfo {
private LinearTypeOrderBuilder defaultOrderBuilder = null;
private LinearTypeOrder defaultTypeOrder = null;
// A reference to the type system.
private final TypeSystemImpl tsi;
/**
* lazily created comparator using the built-in annotation index
*/
private Comparator<TOP> annotationFsComparatorWithoutId = null;
private Comparator<TOP> annotationFsComparatorWithId = null;
private Comparator<TOP> annotationFsComparatorNoTypeWithoutId = null;
private Comparator<TOP> annotationFsComparatorNoTypeWithId = null;
/**
* optimization only - bypasses some shared (among views) initialization if already done
*/
private boolean isSetUpFromBaseCAS = false;
SharedIndexInfo(TypeSystemImpl typeSystem) {
this.tsi = typeSystem;
}
}
/**
* For processing index updates in batch mode when deserializing from a remote service;
* lists of FSs that were added, removed, or reindexed
*
* only used when processing updates in batch mode
*/
private static class ProcessedIndexInfo {
final private Set<TOP> fsAddedToIndex = new ObjHashSet<TOP>(TOP.class, TOP._singleton);
final private Set<TOP> fsDeletedFromIndex = new ObjHashSet<TOP>(TOP.class, TOP._singleton);
final private Set<TOP> fsReindexed = new ObjHashSet<TOP>(TOP.class, TOP._singleton);
}
/**
* Information about all the indexes for a single type. This is kept in an
* List, with the key being the type code.
*/
static class IndexesForType {
/**
* true if one or more of the indexes is a set index
*/
boolean hasSetIndex;
final String typename;
/**
* index of any sorted index or -1 if no sorted index
*/
int aSortedIndex = -1; // -1 or the position of an arbitrary sorted index
int aBagIndex = -1; // -1 or the position of an arbitrary bag index
final ArrayList<FsIndex_iicp<TOP>> indexesForType = new ArrayList<>(0);
IndexesForType(TypeImpl ti) {
this.typename = ti.getName();
}
<T extends TOP> FsIndex_iicp<T> getNonSetIndex() {
if (aSortedIndex < 0 && aBagIndex < 0) { // index is empty!
return null;
}
return (FsIndex_iicp<T>) indexesForType.get((aBagIndex >= 0) ? aBagIndex : aSortedIndex);
}
void add(FsIndex_iicp<TOP> iicp) {
assert typename.equals(iicp.fsIndex_singletype.getType().getName());
final int kind = iicp.fsIndex_singletype.getIndexingStrategy();
int i = indexesForType.size();
switch (kind) {
case FSIndex.BAG_INDEX:
aBagIndex = i;
break;
case FSIndex.DEFAULT_BAG_INDEX:
if (aBagIndex == -1) { // real bag indexes have priority
aBagIndex = i;
}
break;
case FSIndex.SORTED_INDEX:
aSortedIndex = i;
break;
case FSIndex.SET_INDEX:
hasSetIndex = true;
break;
default: Misc.internalError();
}
indexesForType.add(iicp);
}
<T extends FeatureStructure> FsIndex_iicp<T> getIndexExcludingType(int indexingStrategy, FSIndexComparatorImpl comparatorForIndexSpecs) {
for (FsIndex_iicp<TOP> index : indexesForType) {
FsIndex_singletype<TOP> singleTypeIndex = index.fsIndex_singletype;
if (singleTypeIndex.getIndexingStrategy() == indexingStrategy) {
FSIndexComparatorImpl indexComp = singleTypeIndex.getComparatorImplForIndexSpecs();
if (indexComp.equalsWithoutType(comparatorForIndexSpecs)) {
return (FsIndex_iicp<T>) index;
}
}
}
return null;
}
private void removeIndexExcludingType(int indexingStrategy, FSIndexComparatorImpl comparatorForIndexSpecs) {
Iterator<FsIndex_iicp<TOP>> it = indexesForType.iterator();
while (it.hasNext()) {
FsIndex_singletype<TOP> singleTypeIndex = it.next().fsIndex_singletype;
if (singleTypeIndex.getIndexingStrategy() == indexingStrategy) {
FSIndexComparatorImpl indexComp = singleTypeIndex.getComparatorImplForIndexSpecs();
if (indexComp.equalsWithoutType(comparatorForIndexSpecs)) {
it.remove();
break;
}
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("IndexesForType ").append(typename)
.append(" [hasSetIndex=") .append(hasSetIndex)
.append(", aSortedIndex=") .append(aSortedIndex)
.append(", aBagIndex=") .append(aBagIndex)
.append(", indexesForType=").append(indexesForType)
.append("]");
return builder.toString();
}
}
/***** I N S T A N C E V A R I A B L E S *****/
/***** Replicated per view *****/
// A reference to the CAS View.
private final CASImpl cas;
// Is the index repository locked?
private boolean locked = false;
/**
* An array of information about defined indexes, one for each type in the type hierarchy.
* - includes for each type, an unordered list of FsIndex_iicps for
* that type, corresponding to the different index definitions over that type.
*
* The key is the typecode of the type.
*/
final IndexesForType[] indexArray;
IndexesForType getIndexesForType(int typeCode) {
return indexArray[typeCode];
}
IndexesForType getIndexesForUsedType(int i) {
return indexArray[this.usedIndexes.get(i)];
}
// moved from here into individual indexes over each type, for better locality of reference
// /**
// * an array of ints, one for each type in the type hierarchy.
// * Used to enable iterators to detect modifications (adds / removes)
// * to indexes they're iterating over while they're iterating over them.
// * Not private so it can be seen by FSLeafIndexImpl
// */
// final int[] detectIllegalIndexUpdates;
/**
* A map from names to FsIndex_iicps, which represent the index at the
* top-most type declared in the index specification.
* Different names may map to the same iicp.
* The keys are the same across all views, but the values are different, per view
*/
final HashMap<String, FsIndex_iicp<TOP>> name2indexMap;
/**
* speedup for annotation index accessing by type, lazily initialized
*/
final private Map<TypeImpl, FsIndex_annotation<Annotation>> annotationIndexes =
new IdentityHashMap<TypeImpl, FsIndex_annotation<Annotation>>();
// the next are for journaling updates to indexes
final private List<TOP> indexUpdates;
final private BitSet indexUpdateOperation;
private boolean logProcessed;
// Monitor indexes used to optimize getIndexedFS and flush
// set of typecodes corresponding to indexes that are used
final private IntVector usedIndexes;
// one bit per typeCode, indexed by typeCode
// This is only to speed up the test to skip adding an index to the set of "used" ones if it already is used.
final private BitSet isUsed;
// /**
// * Used for maintaining collection of all used iicp's for indexes
// * package scope for setting in index impl flush
// */
// boolean isUsedChanged = true;
//
// /**
// * iicps for all FSs
// */
// private List<FsIndex_iicp<?>> iicps4allFSs = null;
// Monitor which indexes are iterated over, to allow resetting flatIndexes
// final private List<FsIndex_iicp<? extends FeatureStructure>> iteratedSortedIndexes =
// Collections.synchronizedList(new ArrayList<FsIndex_iicp<? extends FeatureStructure>>());
private final SharedIndexInfo sii;
private ProcessedIndexInfo mPii;
/** ----------------------- Support for flattened indexes -----------------*/
// this approach doesn't work, because an iterator over a subtype could do an invalid->valid transition
// while a flattened iterator over a supertype was in existence.
// Subsequent creations of new iterators over the supertype would not notice that the flattened iterator was invalid.
// /**
// * FlattenedIndexValid
// *
// * <p>a BitSet, one per view, indexed by typeCode
// * A bit[i] being on means that a time window has begun (from the moment it is turned on)
// * where a flattened version if it exists) is valid, for index[i] and all its subtypes.
// *
// * <p>Used at iterator creation time to see if a flattened multi-type sorted index needs to be discarded.</p>
// *
// * <p>Bits are initially off.</p>
// *
// * <p>Bit is turned on when a flattened index is successfully created for any index
// * which starts at that type; the flag is only set for the type the flattened index is created for (not its subtypes)</p>
// *
// * Bit is turned off for add/remove to/from index operation, for a type and all its super types.
// * This is facilitated by having a bit set for each type of all its supertypes.
// * This insures that an upper level flattened index is invalidated, even if the lower level
// * gets a new flattened index (and has its is valid bit is set).
// * The reason for this is that any update to a subtype of a type having
// * a flattened index causes that flattened index to become invalid.</p>
// *
// * Multi-threading: Because BitSet is not safe for multithread use, all reading / writing done
// * using itself as a synch lock.
// */
// final ConcurrentBits flattenedIndexValid;
// boolean syncGetFlattenedIndexValid(int i) {
// synchronized (flattenedIndexValid) {
// return flattenedIndexValid.get(i);
// }
// }
@SuppressWarnings("unused")
private FSIndexRepositoryImpl() {
this.cas = null; // because it's final
this.sii = null;
this.name2indexMap = null;
this.indexArray = null;
// this.detectIllegalIndexUpdates = null;
// this.flattenedIndexValid = null;
this.indexUpdates = null;
this.indexUpdateOperation = null;
this.usedIndexes = null;
this.isUsed = null;
// this.isUsedChanged = true;
// this.iicps4allFSs = null;
}
/**
* Constructor.
* Assumption: called first before next constructor call, with the base CAS view
*
* @param cas
*/
FSIndexRepositoryImpl(CASImpl cas) {
this.cas = cas;
this.sii = new SharedIndexInfo(cas.getTypeSystemImpl());
final TypeSystemImpl ts = this.sii.tsi;
// Type counting starts at 1.
final int numTypes = ts.getNumberOfTypes() + 1;
// this.detectIllegalIndexUpdates = new int[numTypes];
// this.flattenedIndexValid = new ConcurrentBits(numTypes);
this.name2indexMap = new HashMap<String, FsIndex_iicp<TOP>>();
this.indexUpdates = new ArrayList<>();
this.indexUpdateOperation = new BitSet();
this.logProcessed = false;
this.indexArray = new IndexesForType[this.sii.tsi.getNumberOfTypes() + 1];
this.usedIndexes = new IntVector();
this.isUsed = new BitSet(numTypes);
// this.isUsedChanged = true;
// this.iicps4allFSs = new ArrayList<>();
init();
}
/**
* Constructor for additional views.
*
* @param cas
* @param baseIndexRepository
*/
FSIndexRepositoryImpl(CASImpl cas, FSIndexRepositoryImpl baseIndexRepo) {
this.cas = cas;
this.sii = baseIndexRepo.sii;
sii.isSetUpFromBaseCAS = true; // bypasses initialization already done
final TypeSystemImpl ts = this.sii.tsi;
// Type counting starts at 1.
final int numTypes = ts.getNumberOfTypes() + 1;
// this.detectIllegalIndexUpdates = new int[numTypes];
// this.flattenedIndexValid = new ConcurrentBits(numTypes);
this.name2indexMap = new HashMap<String, FsIndex_iicp<TOP>>();
this.indexUpdates = new ArrayList<>();
this.indexUpdateOperation = new BitSet();
this.logProcessed = false;
this.indexArray = new IndexesForType[numTypes];
this.usedIndexes = new IntVector();
this.isUsed = new BitSet(numTypes);
// this.isUsedChanged = true;
// this.iicps4allFSs = new ArrayList<>();
init();
// cant do this here because need to have the CAS's ref to this instance set before this is done.
// baseIndexRepo.name2indexMap.keySet().stream().forEach(key -> createIndex(baseIndexRepo, key));
}
/**
* Initialize data. Common initialization called from the constructors.
*/
private void init() {
final TypeSystemImpl ts = this.sii.tsi;
/* **********************************************
* for each type in the TypeSystem,
* create a list of iicp's
* each one corresponding to a defined index
* **********************************************/
final int numTypes = ts.getNumberOfTypes() + 1; // Type counting starts at 1.
// Can't instantiate arrays of generic types, but this is ok for ArrayList.
for (int i = 1; i < numTypes; i++) {
this.indexArray[i] = new IndexesForType(ts.types.get(i));
}
// Arrays.fill(detectIllegalIndexUpdates, Integer.MIN_VALUE);
mPii = new ProcessedIndexInfo();
}
/* ***************
* Create indexes
* ***************/
/**
* create indexes in a view, by copying the baseCas's index repository's definitions
*
* Called when creating or refreshing (after deserializing) a view
*
* @param baseIndexRepo - where the base definitions are
* @param key - the name of the index.
*/
<T extends FeatureStructure>
void createIndex(FSIndexRepositoryImpl baseIndexRepo, String key) {
final FsIndex_singletype<TOP> fsIndex = baseIndexRepo.name2indexMap.get(key).fsIndex_singletype;
createIndexNoQuestionsAsked(fsIndex.getComparatorImplForIndexSpecs(), key, fsIndex.getIndexingStrategy());
}
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#createIndex(FSIndexComparator, String)
*/
@Override
public boolean createIndex(FSIndexComparator comp, String label, int indexType) throws CASAdminException {
if (this.locked) {
throw new CASAdminException(CASAdminException.REPOSITORY_LOCKED);
}
return createIndexNoQuestionsAsked(comp, label, indexType);
}
/**
* This is public only until the xml specifier format supports specifying index kinds (set, bag
* etc.).
*
* @param comp -
* @param label -
* @param indexType -
* @param <T> -
* @return -
*/
public <T extends FeatureStructure>
boolean createIndexNoQuestionsAsked(final FSIndexComparator comp, String label, int indexType) {
FsIndex_iicp<TOP> cp = this.name2indexMap.get(label);
if (cp == null) {
// Create new index
cp = this.addNewIndexRecursive(comp, indexType);
// create a set of feature codes that are in one or more index definitions,
// only once for all cas views
if (!sii.isSetUpFromBaseCAS) {
for (int i = 0, nKeys = comp.getNumberOfKeys(); i < nKeys; i++) {
if (comp.getKeyType(i) == FSIndexComparator.FEATURE_KEY) {
FeatureImpl fi = (FeatureImpl) comp.getKeyFeature(i);
cas.featureCodes_inIndexKeysAdd(fi.getCode()/*, fi.registryIndex*/);
}
}
}
this.name2indexMap.put(label, cp);
return true;
}
// For now, just return false if the label already exists.
return false;
// // An index has previously been registered for this name. We need to
// // compare the types to see if the new addition is compatible with the
// // pre-existing one. There are three cases: the new type can be a sub-type
// // of the old one, in which case we don't need to do anything; or, the
// // new type is a super-type of the old one, in which case we add the new
// // index while keeping the old one; or, there is no subsumption relation,
// // in which case we can't add the index.
// Type oldType = cp.index.getType(); // Get old type from the index.
// Type newType = comp.getType(); // Get new type from comparator.
// if (this.sii.typeSystem.subsumes(oldType, newType)) {
// // We don't need to do anything.
// return true;
// } else if (this.sii.typeSystem.subsumes(newType, oldType)) {
// // Add the index, subsuming the old one.
// cp = this.addIndexRecursive(comp);
// // Replace the old index with the new one in the map.
// this.name2indexMap.put(label, cp);
// return true;
// } else {
// // Can't add index under that name.
// return false;
// }
// }
}
/**
* just for testing purposes
* removes the named index
* Also removes indexes for all subtypes.
* - NOTE this might remove another index definition's index, if it matches
* Only valid if no add-to-index operations have happened.
*
* @param label the name of the index to remove
*/
public void removeIndex(String label) {
FsIndex_iicp<TOP> cp = this.name2indexMap.get(label);
if (cp == null) {
return;
}
int indexingStrategy = cp.getIndexingStrategy();
FSIndexComparatorImpl comp = cp.getComparatorImplForIndexSpecs();
removeIndexBySpec(cp.getTypeCode(), indexingStrategy, comp);
if (indexingStrategy != FSIndex.DEFAULT_BAG_INDEX) {
final TypeImpl type = (TypeImpl) cp.getType();
type.getAllSubtypes().forEachOrdered(subType -> {
FSIndexComparatorImpl compSub = comp.copy();
compSub.setType(type);
removeIndexBySpec(subType.getCode(), indexingStrategy, compSub);
});
}
}
/**
* Reset all indexes, in one view.
*/
public void flush() {
if (!this.locked) {
return;
}
// if (DEBUG) {
// System.out.println("Index Flush Top");
// }
// Do nothing really fast!
if (this.usedIndexes.size() == 0) {
return;
}
annotationIndexes.clear();
isUsed.clear();
// isUsedChanged = true;
// iicps4allFSs.clear();
for (int i = 0; i < usedIndexes.size(); i++) {
int used = this.usedIndexes.get(i);
for (FsIndex_iicp<?> iicp : indexArray[used].indexesForType) {
iicp.fsIndex_singletype.flush();
}
}
// clearIteratedSortedIndexes();
// reset the index update trackers
// resetDetectIllegalIndexUpdates();
this.indexUpdates.clear();
this.indexUpdateOperation.clear();
mPii = new ProcessedIndexInfo();
// this.fsAddedToIndex = new IntSet();
// this.fsDeletedFromIndex = new IntSet();
// this.fsReindexed = new PositiveIntSet_impl();
this.logProcessed = false;
this.usedIndexes.removeAllElements();
}
// // for now, with flattened index optimization disabled, this should be a no-op
// private void clearIteratedSortedIndexes() {
// int sz = iteratedSortedIndexes.size();
// if (DEBUG) {
// System.out.println("Index Flush flatIndex, size = " + sz);
// }
//
//// iteratedSortedIndexes.stream().forEach(iicp -> iicp.flatIndex.flush());
//
// if (DEBUG) {
// if (iteratedSortedIndexes.size() != sz) {
// throw new RuntimeException(
// "Index Flush flatIndex, size not the same, before = " +
// sz + ", after = " + iteratedSortedIndexes.size());
// }
// }
// iteratedSortedIndexes.clear();
// }
/* *****************************************
* Adding indexes to the index repository
*******************************************/
private FsIndex_iicp<TOP> addNewIndex(FSIndexComparatorImpl comparator, int indexType) {
return addNewIndex(comparator, DEFAULT_INDEX_SIZE, indexType);
}
/**
* This is where the actual index gets created.
*/
private <T extends TOP> FsIndex_iicp<T> addNewIndex(final FSIndexComparatorImpl comparator, int initialSize, int indexType) {
FsIndex_iicp<T> iicp = null;
if (isAnnotationIndex(comparator, indexType)) {
FsIndex_singletype<Annotation> index = addNewIndexCore(comparator, initialSize, indexType);
iicp = (FsIndex_iicp<T>) new FsIndex_annotation<Annotation>(index);
} else {
FsIndex_singletype<TOP> index = addNewIndexCore(comparator, initialSize, indexType);
iicp = (FsIndex_iicp<T>) new FsIndex_iicp<TOP>(index);
}
final Type type = comparator.getType();
final int typeCode = ((TypeImpl) type).getCode();
// add indexes so that sorted ones are first, to benefit getAllIndexedFSs
// if (indexType == FSIndex.SORTED_INDEX) {
// this.indexArray[typeCode].add(0, iicp); // shifts rest down
// } else
getIndexesForType(typeCode).add((FsIndex_iicp<TOP>) iicp);
// }
return iicp;
}
boolean isAnnotationIndex(FSIndexComparator c, int indexKind) {
TypeSystemImpl tsi = getTypeSystemImpl();
return indexKind == FSIndex.SORTED_INDEX &&
getTypeSystemImpl().annotType.subsumes((TypeImpl) c.getType()) &&
c.getNumberOfKeys() == 3 &&
c.getKeyType(0) == FSIndexComparator.FEATURE_KEY &&
c.getKeyComparator(0) == FSIndexComparator.STANDARD_COMPARE &&
c.getKeyFeature(0) == tsi.startFeat &&
c.getKeyType(1) == FSIndexComparator.FEATURE_KEY &&
c.getKeyComparator(1) == FSIndexComparator.REVERSE_STANDARD_COMPARE &&
c.getKeyFeature(1) == tsi.endFeat &&
c.getKeyType(2) == FSIndexComparator.TYPE_ORDER_KEY;
}
/**
* The routine which actually creates a new index, for a single type.
*
* @param comparatorForIndexSpecs -
* @param initialSize -
* @param indexingStrategy -
* @return -
*/
<T extends TOP> FsIndex_singletype<T> addNewIndexCore(
final FSIndexComparatorImpl comparatorForIndexSpecs,
int initialSize,
int indexingStrategy) {
final TypeImpl type = (TypeImpl) comparatorForIndexSpecs.getType();
FsIndex_singletype<T> ind;
switch (indexingStrategy) {
case FSIndex.SET_INDEX:
ind = new FsIndex_set_sorted<T>(this.cas, type, indexingStrategy, comparatorForIndexSpecs); // false = is set
break;
// case FSIndex.FLAT_INDEX:
// // this index is only created from another existing index
// throw new UIMARuntimeException(UIMARuntimeException.INTERNAL_ERROR);
case FSIndex.BAG_INDEX:
case FSIndex.DEFAULT_BAG_INDEX:
ind = new FsIndex_bag<T>(this.cas, type, initialSize, indexingStrategy, comparatorForIndexSpecs);
break;
default:
// SORTED_INDEX is the default. We don't throw any errors, if the code is unknown, we just create a sorted index.
ind = new FsIndex_set_sorted<T>(this.cas, type, FSIndex.SORTED_INDEX, comparatorForIndexSpecs); // true = is sorted
break;
}
return ind;
}
/**
* Top level call to add the indexes for a particular index definition
* @param compForIndexSpecs
* @param indexType
* @return the iicp for the top new index
*/
private FsIndex_iicp<TOP> addNewIndexRecursive(FSIndexComparator compForIndexSpecs, int indexType) {
final FSIndexComparatorImpl compCopy = ((FSIndexComparatorImpl)compForIndexSpecs).copy();
return addNewIndexRec(compCopy, indexType);
}
// Will modify comparator, so call with copy.
/**
* Add an index for a type, and then (unless it's a
* DEFAULT_BAG_INDEX), call yourself recursively to add the indexes for all the directly subsumed subtypes.
* @param comp4indexSpecs
* @param indexType
* @return the new iicp for the new index
*/
private FsIndex_iicp<TOP> addNewIndexRec(FSIndexComparatorImpl comp4indexSpecs, int indexType) {
// if this index is already in existence, just reuse it.
FsIndex_iicp<TOP> existing = getIndexBySpec(comp4indexSpecs.getTypeCode(), indexType, comp4indexSpecs);
if (null != existing) {
return existing;
}
final FsIndex_iicp<TOP> iicp = this.addNewIndex(comp4indexSpecs, indexType);
/**
* Maybe add this index for all subtypes (exception: default bag index)
*/
if (indexType == FSIndex.DEFAULT_BAG_INDEX) {
// In this special case, we do not add indexes for subtypes.
return iicp;
}
final TypeImpl superType = (TypeImpl) comp4indexSpecs.getType();
for (Type subType : superType.getDirectSubtypes()) {
FSIndexComparatorImpl compCopy = comp4indexSpecs.copy();
compCopy.setType(subType);
addNewIndexRec(compCopy, indexType);
}
return iicp;
}
// /**
// * Finds an index among iicp's for all defined indexes of a type, such that
// * the type of the index (SET, BAG, SORTED) is the same and
// * the comparatorForIndexSpecs (the keys) are the same
// *
// * Note: the callers of this require that the comp "type" not be part of the comparison -
// * because it will always miscompare, because we're using the
// * "top" level type defined by the index spec but looking for a subtype with the same comparator
// * @param indexes
// * @param comp
// * @param indexType
// * @return the index in the set of iicps for this type for the matching index
// */
// private static final int findIndex(
// ArrayList<FsIndex_iicp<FeatureStructure>> indexes,
// FSIndexComparatorImpl comp,
// int indexingStrategy) {
//
// int i = 0;
//
// for (FsIndex_iicp<FeatureStructure> iicp : indexes) {
// final FsIndex_singletype<FeatureStructure> iicpIndex = iicp.fsIndex_singletype;
// if (iicpIndex.getIndexingStrategy() == indexingStrategy &&
// ((FSIndexComparatorImpl)(iicpIndex.getComparatorForIndexSpecs())).equalsWithoutType(comp)) {
// return i;
// }
// i++;
// }
// return -1;
// }
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#commit()
*/
public void commit() {
// Will create the default type order if it doesn't exist at this point.
getDefaultTypeOrder();
this.locked = true;
}
public LinearTypeOrder getDefaultTypeOrder() {
if (this.sii.defaultTypeOrder == null) {
if (this.sii.defaultOrderBuilder == null) {
this.sii.defaultOrderBuilder = new LinearTypeOrderBuilderImpl(this.sii.tsi);
}
try {
this.sii.defaultTypeOrder = this.sii.defaultOrderBuilder.getOrder();
} catch (final CASException e) {
// Since we're doing this on an existing type names, we can't
// get here.
throw new UIMARuntimeException(UIMARuntimeException.INTERNAL_ERROR, new Object[0], e);
}
}
return this.sii.defaultTypeOrder;
}
public LinearTypeOrderBuilder getDefaultOrderBuilder() {
if (this.sii.defaultOrderBuilder == null) {
this.sii.defaultOrderBuilder = new LinearTypeOrderBuilderImpl(this.sii.tsi);
}
return this.sii.defaultOrderBuilder;
}
void setDefaultTypeOrder(LinearTypeOrder order) {
this.sii.defaultTypeOrder = order;
}
/**
* Managing effective notification that a flat index is no longer valid (at least for new iterators)
*
* Each time an iterator is about to be created, where a flattened index exists, it may be
* invalid because an index update occurred for one or more of its contents. This update may
* be at any of the subtypes.
*
* When an update occurs, that type plus all of its supertypes need to record that any
* already existing flattened index covering these is no longer valid.
*
* This is done in two ways - a slow way and a fast way. The fast way requires an extra bit
* of data, a reset BitSet, to be created. This is created the first time a reset like this is
* needed. This is because in many applications, there may be lots of types that are never
* instantiated or used.
*
* The slow way is to walk up the iicp chain and collect the positions of the bits in the shared
* flattenedIndexValid, and reset those, and as a side effect, construct the fast reset bitset.
* During this walk up, if we find a fast reset bitset, stop the walk there.
*
* To make this work, the iicp has a parent pointer, and a position int set at creation time.
*
*
*
* @return an array of BitSets
* [0] is the flattenedIndexValid bitset, all initialized to false (0)
* [1 - n] depth-first order of getDirectlySubsumedTypes, the "reset"
*/
/**
* Computing the reset bitset lazily
* This is only needed when an index update operation for that type occurs.
*
*/
// private BitSet[] createflattenedIndexValid()
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#getIndexes()
*/
public Iterator<FSIndex<TOP>> getIndexes() {
final ArrayList<FSIndex<TOP>> indexList = new ArrayList<>();
final Iterator<String> it = this.getLabels();
String label;
while (it.hasNext()) {
label = it.next();
indexList.add(getIndex(label));
}
return indexList.iterator();
}
public Iterator<LowLevelIndex> ll_getIndexes() {
ArrayList<LowLevelIndex> indexList = new ArrayList<LowLevelIndex>();
final Iterator<String> it = this.getLabels();
String label;
while (it.hasNext()) {
label = it.next();
indexList.add(ll_getIndex(label));
}
return indexList.iterator();
}
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#getLabels()
*/
public Iterator<String> getLabels() {
return this.name2indexMap.keySet().iterator();
}
/**
* Get the labels for a specific comparator.
*
* @param comp
* The comparator.
* @param <T> type of Feature Structure
* @return An iterator over the labels.
*/
public <T extends FeatureStructure> Iterator<String> getLabels(FSIndexComparator comp) {
final ArrayList<String> labels = new ArrayList<String>();
final Iterator<String> it = this.getLabels();
String label;
while (it.hasNext()) {
label = it.next();
if (this.name2indexMap.get(label).fsIndex_singletype.getComparatorImplForIndexSpecs().equals(comp)) {
labels.add(label);
}
}
return labels.iterator();
}
/**
* @see org.apache.uima.cas.FSIndexRepository#getIndex(String, Type)
* Find iicp by label (for type it was defined for)
* - if not found, return null
*
* Also return null if is an array type of some non-primitive, not TOP (???)
*
* Throw exception if type not subsumed by the top level type
*
* Search all iicps for the type to find the one with same indexing strategy and keys as the iicp for the label
*/
public <T extends FeatureStructure> FSIndex<T> getIndex(String label, Type type) {
// iicp is for the type the index was defined for
final FsIndex_iicp<TOP> iicp = this.name2indexMap.get(label);
if (iicp == null) {
return null;
}
// Why is this necessary?
// probably because we don't support indexes over FSArray<some-particular-type>
if (type.isArray()) {
final Type componentType = type.getComponentType();
if ((componentType != null) && !componentType.isPrimitive()
&& !componentType.getName().equals(CAS.TYPE_NAME_TOP)) {
return null;
}
}
final TypeImpl indexType = iicp.fsIndex_singletype.getTypeImpl();
final TypeImpl ti = (TypeImpl) type;
if (!indexType.subsumes(ti)) {
throw new CASRuntimeException(CASRuntimeException.TYPE_NOT_IN_INDEX, label, type.getName(), indexType.getName());
}
// Since we found an index for the correct type, and
// named indexes at creation time create all their subtype iicps, find() must return a
// valid result
return (FSIndex<T>) this.getIndexBySpec(ti.getCode(), iicp.getIndexingStrategy(), iicp.getComparatorImplForIndexSpecs());
}
/**
* @see org.apache.uima.cas.FSIndexRepository#getIndex(String)
*/
@SuppressWarnings("unchecked")
public <T extends FeatureStructure> LowLevelIndex<T> getIndex(String label) {
return (LowLevelIndex<T>) this.name2indexMap.get(label);
}
/**
* Remove all instances of a particular type (but not its subtypes) from all indexes
* @param type -
*/
public void removeAllExcludingSubtypes(Type type) {
final int typeCode = ((TypeImpl) type).getCode();
// incrementIllegalIndexUpdateDetector(typeCode);
// get a list of all indexes defined over this type
// Includes indexes defined on supertypes of this type
final ArrayList<FsIndex_iicp<TOP>> allIndexesForType = getIndexesForType(typeCode).indexesForType;
for (FsIndex_iicp<? extends FeatureStructure> iicp : allIndexesForType) {
iicp.fsIndex_singletype.removeAll();
}
}
/**
* Remove all instances of a particular type (including its subtypes) from all indexes
* @param type Type to remove (including all its subtypes) from this particular view.
*/
public void removeAllIncludingSubtypes(Type type) {
removeAllExcludingSubtypes(type);
List<Type> subtypes = this.sii.tsi.getDirectSubtypes(type);
for (Type subtype : subtypes) {
removeAllIncludingSubtypes(subtype);
}
}
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#createComparator()
*/
public FSIndexComparator createComparator() {
return new FSIndexComparatorImpl();
}
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#isCommitted()
*/
public boolean isCommitted() {
return this.locked;
}
/**
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#createIndex(org.apache.uima.cas.admin.FSIndexComparator,
* java.lang.String)
*/
public boolean createIndex(FSIndexComparator comp, String label) throws CASAdminException {
return createIndex(comp, label, FSIndex.SORTED_INDEX);
}
// ///////////////////////////////////////////////////////////////////////////
// Serialization support
// /**
// * For one particular view (the one associated with this instance of FsIndexRepositoryImpl),
// * return an array containing all FSs in any defined index, in this view.
// * This is intended to be used for serialization.
// *
// * The order in which FSs occur in the array does not reflect the order in which they
// * were added to the repository.
// *
// * @param <T> type of Feature Structure
// * @return a List of all FSs in any defined index, in this view.
// */
// public <T extends FeatureStructure> List<T> getIndexedFSs4Serializers() {
//
// final ArrayList<TOP> v = new ArrayList<>(); // accumulates fsAddrs from various indexes
//
// /* Iterate over index by type, with something in there
// * and dump all the fss found for that type (excluding subtypes) into v
// * bag preferred over sorted;
// */
// for (int i = 0; i < this.usedIndexes.size(); i++) {
//// // debug
//// int vs1 = v.size();
// getNonSetSingleIndexForUsedType(i).bulkAddTo(v);
//// for (int di = vs1; di < v.size(); di ++) { // debug
//// assert v.get(di) != null; // debug verify not null
//// }
// }
//
// return (List<T>) v;
// }
/**
* For this view, walk the indexed FSs in arbitrary order.
* @param action the action to do on each FS
*/
public void walkIndexedFSs(Consumer<TOP> action) {
for (int i = 0; i < this.usedIndexes.size(); i++) {
for (TOP fs : getNonSetSingleIndexForUsedType(i)) {
action.accept(fs);
}
}
}
/**
* For this view, walk the indexed FSs, sorted by id
* (e.g. creation time)
* @param action -
*/
public void walkSortedIndexedFSs(Consumer<TOP> action) {
List<TOP> fss = new ArrayList<>();
for (int i = 0; i < this.usedIndexes.size(); i++) {
for (TOP fs : getNonSetSingleIndexForUsedType(i)) {
fss.add(fs);
}
}
Collections.sort(fss, (fs1, fs2) -> Integer.compare(fs1._id, fs2._id));
for (TOP fs : fss) {
action.accept(fs);
}
}
public FsIndex_singletype<TOP> getNonSetSingleIndexForType(int typecode) {
return getIndexesForType(typecode).getNonSetIndex().fsIndex_singletype;
}
public FsIndex_singletype<TOP> getNonSetSingleIndexForUsedType(int i) {
return getIndexesForUsedType(i).getNonSetIndex().fsIndex_singletype;
}
// /**
// * walk routine for all views for all reachable fss
// * Variations: split apart above/below line ?
// * do type filtering
// *
// * because java doesn't support tail recursion use a stack
// * @param action
// */
// public <T extends TOP> void walkReachableFSs(Consumer<T> action) {
// PositiveIntSet alreadySeen = new PositiveIntSet_impl();
// Deque<TOP> toWalk = new ArrayDeque<>();
// walkReachableFSs(action, alreadySeen, toWalk);
// }
//
// private <T extends TOP> void walkReachableFSs(
// Consumer<T> action,
// PositiveIntSet alreadySeen,
// Deque<TOP> toWalk) {
// cas.getBaseCAS().indexRepository.<T>walkIndexedFSs(fs -> {
// alreadySeen.add(fs._id);
// action.accept(fs);
// }); // walk the sofas
// cas.forAllIndexRepos(ir -> ir.walkReachableFSsOneIndexRepo(action, alreadySeen, toWalk));
// }
//
// public <T extends TOP> void walkReachableFSsOneIndexRepo(
// Consumer<T> action,
// PositiveIntSet alreadySeen,
// Deque<TOP> toWalk) {
//
// for (int i = 0; i < this.usedIndexes.size(); i++) {
// FsIndex_singletype<T> index = indexArray[this.usedIndexes.get(i)].<T>getNonSetIndex().fsIndex_singletype;
// for (T fs : index) {
// action.accept(fs);
// }
// }
// }
/* *****************************************
* Adding/removing FS to/from the index
* *****************************************/
public void addFS(int fsRef) {
ll_addFS(fsRef);
}
public void ll_addFS(int fsRef) {
addFS_common(cas.getFsFromId_checked(fsRef), false); // false === is not an addback call
}
public void ll_removeFS(int fsRef) {
removeFS(cas.getFsFromId_checked(fsRef));
}
/**
* @see org.apache.uima.cas.FSIndexRepository#addFS(org.apache.uima.cas.FeatureStructure)
*/
public <T extends FeatureStructure> void addFS(T fs) {
addFS_common((TOP)fs, false);
}
// private void incrementIllegalIndexUpdateDetector(int typeCode) {
// this.detectIllegalIndexUpdates[typeCode] ++;
// }
/**
* @see org.apache.uima.cas.FSIndexRepository#removeFS(org.apache.uima.cas.FeatureStructure)
*/
public void removeFS(FeatureStructure fs) {
removeFS_ret((TOP) fs, INCLUDE_BAG_INDEXES);
if (fs instanceof AnnotationBase) {
// fs can only be in 1 view, and has been removed from *all* indexes in that view
((FeatureStructureImplC)fs)._resetInSetSortedIndex();
}
}
public void removeFS(int fsRef) {
removeFS(cas.getFsFromId_checked(fsRef));
}
/*
* Only used by test cases
* Others call getDefaultOrderBuilder
*
* This method always returns the newly created object which may be different
* (not identical == ) to the this.defaultOrderBuilder.
* Not sure if that's important or a small bug... Oct 2014 schor
* (non-Javadoc)
*
* @see org.apache.uima.cas.admin.FSIndexRepositoryMgr#createTypeSortOrder()
*/
public LinearTypeOrderBuilder createTypeSortOrder() {
final LinearTypeOrderBuilder orderBuilder = new LinearTypeOrderBuilderImpl(this.sii.tsi);
if (this.sii.defaultOrderBuilder == null) {
this.sii.defaultOrderBuilder = orderBuilder;
}
return orderBuilder;
}
public <T extends FeatureStructure> LowLevelIndex<T> ll_getIndex(String indexName) {
return (LowLevelIndex<T>) getIndex(indexName);
}
public <T extends FeatureStructure> LowLevelIndex<T> ll_getIndex(String indexName, int typeCode) {
final TypeSystemImpl tsi = this.sii.tsi;
if (!tsi.isType(typeCode) || !this.cas.ll_isRefType(typeCode)) {
throw new LowLevelException(LowLevelException.INVALID_INDEX_TYPE, Integer.toString(typeCode));
}
return (LowLevelIndex<T>) getIndex(indexName, tsi.ll_getTypeForCode(typeCode));
}
public final void ll_addFS(int fsRef, boolean doChecks) {
ll_addFS(fsRef);
}
public <T extends TOP> void addback(T fs) {
addFS_common(fs, true);
}
private <T extends TOP> void addFS_common(T fs, boolean isAddback) {
if (fs._isPearTrampoline()) {
fs = fs._casView.getBaseFsFromTrampoline(fs);
}
TypeImpl ti = ((FeatureStructureImplC)fs)._getTypeImpl();
final int typeCode = ti.getCode();
if (typeCode != TypeSystemConstants.sofaTypeCode && cas.isBaseCas()) {
throw new CASRuntimeException(CASRuntimeException.ILLEGAL_ADD_TO_INDEX_IN_BASE_CAS, fs, cas);
}
// https://issues.apache.org/jira/browse/UIMA-4099
// skip test for wrong view if addback, etc.
if (CASImpl.traceCow) {
fs._casView.traceIndexMod(true, fs, isAddback);
}
if (!isAddback && (!IS_DISABLE_ENHANCED_WRONG_INDEX_CHECK) && ti.isAnnotationBaseType()) {
Sofa sofa = ((AnnotationBase)fs).getSofa();
if (sofa == null) {
throw new CASRuntimeException(
CASRuntimeException.SOFAREF_NOT_SET, fs.toString(3));
}
// Check that the annotationBase FS is being added to the proper Cas View
CASImpl indexView = fs._getView();
if (indexView.getIndexRepository() != this) {
/* Error - the Annotation "{0}" is over view "{1}" and cannot be added to indexes associated with
* the different view "{2}" */
throw new CASRuntimeException(CASRuntimeException.ANNOTATION_IN_WRONG_INDEX,
fs.toString(), indexView.getViewName(), cas.getViewName());
}
}
// indicate this type's indexes are being modified
// in case an iterator is simultaneously active over this type
// incrementIllegalIndexUpdateDetector(typeCode);
// Get the indexes for the type.
final ArrayList<FsIndex_iicp<TOP>> indexes = getIndexesForType(typeCode).indexesForType;
// Add fsRef to all indexes.
boolean noIndexOrOnlySetindexes = true;
boolean setOrSorted = false; // set to true if at least one set or sorted index found
for (FsIndex_iicp<TOP> iicp : indexes) {
// the indexes for the type are over the type and its subtypes.
final int indexingStrategy = iicp.fsIndex_singletype.getIndexingStrategy();
if (isAddback) {
if (indexingStrategy == FSIndex.BAG_INDEX) {
continue; // skip adding back to bags - because removes are skipped for bags
}
}
iicp.fsIndex_singletype.insert(fs);
// remember if we get any index other than set by turning this false;
if (noIndexOrOnlySetindexes) {
noIndexOrOnlySetindexes = indexingStrategy == FSIndex.SET_INDEX;
}
// remember if we get any set or sorted index by turning this true
if (setOrSorted == false && indexingStrategy != FSIndex.BAG_INDEX) {
setOrSorted = true;
}
}
// log even if added back, because remove logs remove, and might want to know it was "reindexed"
if (this.cas.getCurrentMark() != null) {
logIndexOperation(fs, true);
}
if (setOrSorted) { // only set this bit if this fs is in 1 or more set or sorted indexes
fs._setInSetSortedIndexed();
}
if (isAddback) { return; }
// https://issues.apache.org/jira/browse/UIMA-4111
if (noIndexOrOnlySetindexes) {
// lazily create a default bag index for this type
final Type type = this.sii.tsi.ll_getTypeForCode(typeCode);
final String defIndexName = getAutoIndexNameForType(type);
final FSIndexComparator comparator = createComparator(); // empty comparator
comparator.setType(type);
createIndexNoQuestionsAsked(comparator, defIndexName, FSIndex.DEFAULT_BAG_INDEX);
// add the FS to the bag index
// which is the last one added
((FsIndex_singletype<T>)(indexes.get(indexes.size() - 1)).fsIndex_singletype).insert(fs);
}
if (!this.isUsed.get(typeCode)) {
// mark this type as being in some indexes
this.isUsed.set(typeCode);
// this.isUsedChanged = true;
this.usedIndexes.add(typeCode);
}
}
private static final String getAutoIndexNameForType(Type type) {
return "_" + type.getName() + "_DefaultBagGeneratedIndex";
}
/**
* Common remove FS code; all remove operations call this, except bulk remove (flush and removeall...)
* Removes FS from all indexes in this view (except bag if skipBagIndexes is true)
* @param fs the fs to remove
* @param skipBagIndexes set true by protect-indexes style of temporary removal
* @return true if it was removed
*/
boolean removeFS_ret(TOP fs, boolean skipBagIndexes) {
if (skipBagIndexes && !fs._inSetSortedIndex()) {
return false;
}
final int typeCode = fs._getTypeImpl().getCode();
final IndexesForType i4t = getIndexesForType(typeCode);
final ArrayList<FsIndex_iicp<TOP>> indexes4type = i4t.indexesForType;
boolean wasRemoved = false;
/**
* some optimization speedup
* - skip remove if
*
* -- there is no sorted index AND
* -- there is no bag index and no set index (no index at all) OR
* -- this is a bag index but it doesn't have this fs
*/
if (i4t.aSortedIndex < 0) {
int bi = i4t.aBagIndex; // >= 0 if there is a bag index
if (bi < 0 && !i4t.hasSetIndex) {
return false; // no indexes defined for this type
}
if (bi >= 0 && !i4t.indexesForType.get(bi).fsIndex_singletype.contains(fs)) {
return false; // not in defined bag index
}
}
/**
* Actual remove loop over all indexes for this type in this view
*/
for (FsIndex_iicp<TOP> iicp : indexes4type) {
FsIndex_singletype<TOP> st = iicp.fsIndex_singletype;
if (skipBagIndexes && !st.isSetOrSorted()) {
continue;
}
if (st.deleteFS(fs)) {
wasRemoved = true;
}
}
//
// int nbrRemoved = idxList.stream().map(iicp -> iicp.fsIndex_singletype)
// .filter(st -> (!skipBagIndexes) || st.isSetOrSorted())
// .mapToInt(st -> st.deleteFS(fs) ? 1 : 0).sum();
if (wasRemoved) {
// incrementIllegalIndexUpdateDetector(typeCode);
if (this.cas.getCurrentMark() != null) {
logIndexOperation(fs, ITEM_REMOVED_FROM_INDEX);
}
// oops, might still be indexed in other views if not instance of AnnotationBase
// reset in caller (removeFromIndexAnyView)
// if (skipBagIndexes || // means called for remove only from all corruptable indexes
// fs instanceof AnnotationBase) { // means only indexed in this view
// fs._resetInSetSortedIndex();
// }
}
if (CASImpl.traceCow) {
this.cas.traceIndexMod(false, fs, skipBagIndexes);
}
return wasRemoved;
}
public <T extends FeatureStructure> LowLevelIterator<T> ll_getAllIndexedFS(Type type) {
return (LowLevelIterator<T>) getAllIndexedFS(type);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.FSIndexRepository#getAllIndexedFS(org.apache.uima.cas.Type)
*/
public <T extends FeatureStructure> LowLevelIterator<T> getAllIndexedFS(Type type) {
final ArrayList<LowLevelIterator<T>> iteratorList = new ArrayList<>();
getAllIndexedFS(type, iteratorList);
final int iteratorListSize = iteratorList.size();
if (iteratorListSize == 0) {
return (LowLevelIterator<T>) LowLevelIterator.FS_ITERATOR_LOW_LEVEL_EMPTY;
}
if (iteratorListSize == 1) {
return iteratorList.get(0);
}
LowLevelIterator<T>[] ia = new LowLevelIterator[iteratorListSize];
return new FsIterator_aggregation_common<T>(iteratorList.toArray(ia), null, null);
}
private final <T extends FeatureStructure> void getAllIndexedFS(Type type, List<LowLevelIterator<T>> iteratorList) {
// Strategy: go through the list of all indexes for this type.
// The list is intentionally ordered when created to have "SORTED" indexes come first.
//
// Check all of the Sorted indexes to see if any have a flatten iterator, and if found use that.
//
// If no sorted, flattened indexes exist, use any sorted index, but run as unordered to avoid rattling iterators
//
// If no sorted index exists, use Bag or Default-bag index. If default-bag, call recursively to get sub-indexes.
//
// Note that a default bag index is guaranteed to exist if any FS of Type type were added to the indexes
// and only a SET index was defined, see https://issues.apache.org/jira/browse/UIMA-4111
// get all indexes for this type and compute iicps4allFSs
TypeImpl ti = (TypeImpl)type;
if (isUsed.get(ti.getCode())) {
FsIndex_iicp<T> iicp = (FsIndex_iicp<T>) getIndexesForType(ti.getCode()).getNonSetIndex();
// iicps4allFSs.add(iicp);
if (null != iicp && !iicp.isEmpty()) {
LowLevelIterator<T> it = (iicp.getIndexingStrategy() == FSIndex.SORTED_INDEX)
? (LowLevelIterator<T>)iicp.iterator(true, true) // order not needed, ignore type
: (LowLevelIterator<T>)iicp.iterator();
iteratorList.add(it);
if (iicp.isDefaultBagIndex()) {
// We found one of the special auto-indexes which don't inherit down the tree. So, we
// manually need to traverse the inheritance tree to look for more indexes. Note that
// this is not necessary when we have a regular index
addDirectSubtypes(ti, iteratorList);
}
return;
}
}
// No index for this type was found at all.
// Example: You ask for an iterator over "TOP", but no instances of TOP are created,
// and no index over TOP was ever created.
// Since the auto-indexes are created on demand for
// each type, there may be gaps in the inheritance chain. So keep descending the inheritance
// tree looking for relevant indexes.
addDirectSubtypes(ti, iteratorList);
}
private <T extends FeatureStructure> void addDirectSubtypes(TypeImpl type, List<LowLevelIterator<T>> iteratorList) {
for (TypeImpl subType : type.getDirectSubtypes()) {
getAllIndexedFS(subType, iteratorList);
}
// ((TypeImpl)type).getDirectSubtypes().stream().forEach(subType -> getAllIndexedFS(subType, iteratorList));
}
// do this in index creation order
// needed for backwards compatibility
// https://issues.apache.org/jira/browse/UIMA-5603 see comment toward end
public Collection<TOP> getIndexedFSs() {
final ArrayList<CopyOnWriteIndexPart<TOP>> indexes = new ArrayList<>();
for (int i = 0; i < this.usedIndexes.size(); i++) {
FsIndex_singletype<TOP> idx = getNonSetSingleIndexForUsedType(i);
if (idx.size() > 0) {
indexes.add(idx.getNonNullCow());
}
}
return getCollectionFromCows(indexes);
}
public <T extends TOP> Collection<T> getIndexedFSs(Class<T> clazz) {
return getIndexedFSs(cas.getCasType(clazz));
}
/**
* @param type the type of Feature Structures to include (including subtypes)
* @return an unmodifiable, unordered set of all indexed (in this view) Feature Structures
* of the specified type (including subtypes)
*/
public <T extends TOP> Collection<T> getIndexedFSs(Type type) {
// collect CopyOnWriteIndexPart s for all index parts for type and its subtypes
final ArrayList<CopyOnWriteIndexPart<T>> indexes = new ArrayList<>();
TypeImpl ti = (TypeImpl) type;
collectCowIndexParts(ti, indexes);
return getCollectionFromCows(indexes);
}
private <T extends TOP> Collection<T> getCollectionFromCows(ArrayList<CopyOnWriteIndexPart<T>> indexes) {
if (indexes.size() == 0) {
return Collections.emptySet();
}
return new AbstractCollection<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
final int indexesSize = indexes.size();
int indexesIndex = 0;
Iterator<T> it = indexes.get(0).iterator();
@Override
public boolean hasNext() {
return indexesIndex < indexesSize;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T v = it.next();
if (!it.hasNext()) {
indexesIndex++;
if (indexesIndex == indexesSize) {
return v;
}
it = indexes.get(indexesIndex).iterator();
}
return v;
}
};
}
@Override
public int size() {
int r = 0;
for (CopyOnWriteIndexPart<T> cow : indexes) {
r += cow.size();
}
return r;
}
@Override
public TOP[] toArray() {
TOP[] r = new TOP[size()];
int i = 0;
for (CopyOnWriteIndexPart<T> idx : indexes) {
i = idx.copyToArray(r, i);
}
return r;
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#toArray(java.lang.Object[])
*/
@Override
public <U> U[] toArray(U[] r) {
int i = 0;
for (CopyOnWriteIndexPart<T> idx : indexes) {
i = idx.copyToArray((TOP[]) r, i);
}
return r;
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#isEmpty()
*/
@Override
public boolean isEmpty() {
return indexes.isEmpty();
}
};
}
private <T extends TOP> void collectCowIndexParts(TypeImpl ti, ArrayList<CopyOnWriteIndexPart<T>> indexes) {
FsIndex_iicp<T> iicp;
if (!isUsed.get(ti.getCode()) ||
(iicp = getIndexesForType(ti.getCode()).getNonSetIndex()) == null ||
iicp.isEmpty()) { // could be used, but now empty
// No index for this type was found at all.
// Example: You ask for an iterator over "TOP", but no instances of TOP are created,
// and no index over TOP was ever created.
// Since the auto-indexes are created on demand for
// each type, there may be gaps in the inheritance chain. So keep descending the inheritance
// tree looking for relevant indexes.
ti.getDirectSubtypes().forEach(type -> collectCowIndexParts(type, indexes));
return;
}
if (iicp.isDefaultBagIndex()) {
if (iicp.getFsIndex_singleType().size() > 0) {
indexes.add(iicp.getFsIndex_singleType().getNonNullCow());
}
ti.getDirectSubtypes().forEach(type -> collectCowIndexParts(type, indexes));
} else {
iicp.collectCowIndexParts(indexes);
}
}
/**
* Stream instances of all of the non-empty indexes themselves
* @param type - the type to filter the indexes with
* @return all of the non-empty indexes, one for each sorted or default bag per type
*/
public Stream<FsIndex_singletype<TOP>> streamNonEmptyIndexes(Type type) {
TypeImpl ti = (TypeImpl) type;
if (!isUsed.get(ti.getCode())) {
return streamNonEmptyDirectSubtypes(ti);
}
FsIndex_iicp<TOP> iicp = getIndexesForType(ti.getCode()).getNonSetIndex();
if (null == iicp || iicp.isEmpty()) {
return Stream.empty();
}
Stream<FsIndex_singletype<TOP>> iicpIndexesStream = iicp.streamNonEmptyIndexes();
return iicp.isDefaultBagIndex()
? Stream.concat(iicpIndexesStream, streamNonEmptyDirectSubtypes(ti))
: iicpIndexesStream;
}
public Stream<FsIndex_singletype<TOP>> streamNonEmptyIndexes(Class<? extends TOP> clazz) {
return streamNonEmptyIndexes(getCasImpl().getCasType(clazz));
}
private Stream<FsIndex_singletype<TOP>> streamNonEmptyDirectSubtypes(TypeImpl ti) {
Stream<FsIndex_singletype<TOP>> r = null;
for (TypeImpl subType : ti.getDirectSubtypes()) {
r = (r == null)
? streamNonEmptyIndexes(subType)
: Stream.concat(r, streamNonEmptyIndexes(subType));
}
return (r == null) ? Stream.empty() : r;
}
// next method dropped - rather than seeing if something is in the index, and then
// later removing it (two lookups), we just conditionally remove it
// /**
// * This is used to see if a FS which has a key feature being modified
// * could corrupt an index in this view. It returns true if found
// * (sometimes it returns true, even if strictly speaking, there is
// * no chance of corruption - see below)
// *
// * It does this by seeing if this FS is indexed by one or more Set or Sorted
// * indexes. No need to check bag indexes - they have no keys, so can't be corrupted by key value changes.
// *
// * Any sorted index indexes all FSs, so if we find a sorted index, return true if it contains the fs, false otherwise.
// *
// * If there are no sorted indexes, if there are one or more set indexes,
// * return true if any of the set indexes have the fs, false if none have it.
// *
// * To speed this up, we keep the set of indexes defined for a type with two additional pieces of meta information:
// * 1) the first sorted index (or -1 if none are sorted).
// * 2) a boolean - if there exist any set indexes for this type.
// *
// *
// * @param fs the FS to see if it is in some index that could be corrupted by a key feature value change
// * @return true if this fs is found in a Set or Sorted index.
// */
// public boolean isInSetOrSortedIndexInThisView(FeatureStructureImplC fs) {
// final TypeImpl ti = fs._getTypeImpl();
//
// final IndexesForType i4t = indexArray[ti.getCode()];
//
// int si = i4t.aSortedIndex;
// if (si >= 0) { // have sorted index
// return i4t.indexesForType.get(si).fsIndex_singletype.contains(fs);
// }
//
// int bi = i4t.aBagIndex;
// if (bi >= 0) { // have a bag index
// if (i4t.indexesForType.get(si).fsIndex_singletype.contains(fs) == false) {
// // not in the index, return false
// return false;
// }
// // is in bag index, there are no sort indexes
// if (i4t.hasSetIndex) {
// return true; // approximation - maybe it isn't in the set index.
// }
// // no sorted, no set indexes
// return false;
// }
//
// // no sort, no bag index
// // because bag is present if any item indexed, this means nothing in the index
// return false;
// }
// see instead removeAndRecord in CASImpl
// /**
// * This is called when it has been determined that:
// * - the fs might be in some indexes
// * - a change is being made to 1 or more feature values, and those features are being used as keys in one or more indexes
// *
// * This happens
// * - in normal operation, when setting feature values.
// * - when deserializing a FS using delta CAS which could be modifying an existing one (below the line).
// *
// * Although one could check each index's keys against the value which was changing, this isn't done because
// * - it would slow things down
// * - this "automatic" removal should be the exception. Users are advised to manually remove the FSs from indexes themselves
// * before updating features which might be used as keys.
// *
// * The removal skips removing FSs from bag or default-bag indexes, because these have no keys.
// * The add-back also refrains from adding the FSs back to bag indexes.
// *
// * The current implementation does not try to determine if any keys are updated with different values,
// * it just assumes one or more are.
// *
// * If the view has nothing other than bag indexes for this type, return false without doing any remove
// *
// * @param afs - the FS to see if it is in some index that could be corrupted by a key feature value change
// * @return true if this fs was in the indexes and will need to be added back.
// */
// boolean removeIfInCorrputableIndexInThisView(FeatureStructure afs) {
// return removeFS_ret((TOP) afs, SKIP_BAG_INDEXES);
//// TOP fs = (TOP) afs;
//// TypeImpl ti = fs._getTypeImpl();
//// final IndexesForType i4t = getIndexesForType(ti.getCode());
////
//// int si = i4t.aSortedIndex;
//// if (si >= 0) { // then we have a sorted index
//// return removeFS_ret(fs, SKIP_BAG_INDEXES);
//// }
////
//// int bi = i4t.aBagIndex;
//// if (bi >= 0) { // have one or more bag indexes including default bag index, for this type
//// // use the bag index to stop if it doesn't contain the FS, because bag contains testing is fast..
//// if (!i4t.indexesForType.get(bi).fsIndex_singletype.contains(fs)) {
//// return false;
//// }
//// if (i4t.hasSetIndex) {
//// return removeFS_ret(fs, SKIP_BAG_INDEXES);
//// }
//// }
////
//// // have no bag index, no sort index (implies index is empty)
//// return false;
// }
// /**
// * reset the flat index is valid for this type
// */
// private void indexUpdated(int typeCode) {
// flattenedIndexValid.clear(typeCode);
// }
/**
* returns the annotation index for a type which is Annotation or a subtype of it.
* remembers answer in hashmap annotationIndexes, key = TypeImpl
* @param typeCode
* @return the index for that type
*/
<T extends AnnotationFS> FsIndex_annotation<T> getAnnotationIndex(TypeImpl ti) {
// assert(ti.isAnnotationType());
FsIndex_annotation<Annotation> r = annotationIndexes.get(ti);
if (r != null) {
return (FsIndex_annotation<T>) r;
}
FsIndex_annotation r1 = (FsIndex_annotation) getIndex(CAS.STD_ANNOTATION_INDEX, ti);
r = r1;
annotationIndexes.put(ti, r);
return (FsIndex_annotation<T>) r;
}
private <T extends TOP> void logIndexOperation(T fs, boolean added) {
this.indexUpdates.add(fs);
if (added) {
this.indexUpdateOperation.set(this.indexUpdates.size() - 1, added); // operation was "add"
}
this.logProcessed = false;
}
// Delta Serialization support
/**
* Go through the journal, and use those entries to update
* added, deleted, and reindexed lists
* in such a way as to guarantee:
* a FS is in only one of these lists, (or in none)
*
* For a journal "add-to-indexes" event:
* fs in "deleted": remove from "deleted", add to "reindexed"
* fs in "reindexed": do nothing
* fs in "added": do nothing
* fs not in any of these: add to "added"
*
* For a journal "remove-from-indexes" event:
* fs in "added": remove from "added" (don't add to "deleted")
* fs in "reindexed": remove from "reindexed" and add to "deleted")
* fs in "deleted": do nothing
* fs not in any of these: add to "deleted"
*
* The journal is cleared after processing.
*/
private void processIndexUpdates() {
final ProcessedIndexInfo pii = mPii;
final int len = this.indexUpdates.size();
for (int i = 0; i < len; i++) {
final TOP fs = this.indexUpdates.get(i);
final boolean added = this.indexUpdateOperation.get(i);
if (added) {
boolean wasRemoved = pii.fsDeletedFromIndex.remove(fs);
if (wasRemoved) {
pii.fsReindexed.add(fs);
} else if (pii.fsReindexed.contains(fs)) {
continue; // add on top of reindex is ignored
} else { // wasn't in deleted, wasn't in reindexed
pii.fsAddedToIndex.add(fs);
}
} else {
// operation was remove-from-indexes
boolean wasRemoved = pii.fsAddedToIndex.remove(fs);
if (!wasRemoved) {
pii.fsReindexed.remove(fs);
pii.fsDeletedFromIndex.add(fs);
}
}
}
this.logProcessed = true;
this.indexUpdates.clear();
this.indexUpdateOperation.clear();
}
public Set<TOP> getUpdatedFSs(Set<TOP> items) {
if (!this.logProcessed) {
processIndexUpdates();
}
return items;
}
public Set<TOP> getAddedFSs() {
return getUpdatedFSs(mPii.fsAddedToIndex);
}
public Set<TOP> getDeletedFSs() {
return getUpdatedFSs(mPii.fsDeletedFromIndex);
}
public Set<TOP> getReindexedFSs() {
return getUpdatedFSs(mPii.fsReindexed);
}
public boolean isModified() {
if (!this.logProcessed) {
processIndexUpdates();
}
final ProcessedIndexInfo pii = mPii;
return ((pii.fsAddedToIndex.size() > 0) || (pii.fsDeletedFromIndex.size() > 0) || (pii.fsReindexed
.size() > 0));
}
@Override
public String toString() {
return this.getClass().getSimpleName() + " [" + cas + "]";
}
// public Comparator<AnnotationFS> getAnnotationComparator() {
// if (null == this.sii.annotationComparator) {
// @SuppressWarnings("unchecked")
// final FsIndex_iicp<AnnotationFS> iicp =
// (FsIndex_iicp<AnnotationFS>) this.name2indexMap.get(CAS.STD_ANNOTATION_INDEX);
// this.sii.annotationComparator = (FSIntArrayIndex<AnnotationFS>)(iicp.fsIndex_singletype);
// }
// return this.sii.annotationComparator;
// }
public Comparator<TOP> getAnnotationFsComparator(FSComparators withId, FSComparators withTypeOrder) {
Comparator<TOP> r = getCachedComparator(withId, withTypeOrder);
if (r == null) {
r = createAnnotationFsComparator(withId, withTypeOrder);
setCachedComparator(withId, withTypeOrder, r);
}
return r;
}
private Comparator<TOP> createAnnotationFsComparator(FSComparators withId, FSComparators withTypeOrder) {
LinearTypeOrder lto = (withTypeOrder == FSComparators.WITH_TYPE_ORDER) ? getDefaultTypeOrder() : null;
if (withId == FSComparators.WITH_ID) {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
return (fs1, fs2) -> (fs1 == fs2) ? 0 : ((Annotation) fs1).compareAnnotationWithId((Annotation) fs2, lto);
} else {
return (fs1, fs2) -> (fs1 == fs2) ? 0 : ((Annotation) fs1).compareAnnotationWithId((Annotation) fs2);
}
} else {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
return (fs1, fs2) -> (fs1 == fs2) ? 0 : ((Annotation) fs1).compareAnnotation((Annotation) fs2, lto);
} else {
return (fs1, fs2) -> (fs1 == fs2) ? 0 : ((Annotation) fs1).compareAnnotation((Annotation) fs2);
}
}
}
public Comparator<TOP> getAnnotationFsComparatorWithoutId() {
Comparator<TOP> r = this.sii.annotationFsComparatorWithoutId;
// lazy creation
if (null != r) {
return r;
}
return createAnnotationFsComparator();
}
Comparator<TOP> getAnnotationFsComparatorWithId() {
Comparator<TOP> r = this.sii.annotationFsComparatorWithId;
// lazy creation
if (null != r) {
return r;
}
return createAnnotationFsComparatorWithId();
}
private Comparator<TOP> createAnnotationFsComparator() {
final LinearTypeOrder lto = getDefaultTypeOrder(); // used as constant in comparator
if (!V2_ANNOTATION_COMPARE_TYPE_ORDER && lto.isEmptyTypeOrder()) {
return this.sii.annotationFsComparatorWithoutId = (fsx1, fsx2) -> {
if (fsx1 == fsx2) return 0;
Annotation fs1 = (Annotation) fsx1;
Annotation fs2 = (Annotation) fsx2;
return fs1.compareAnnotation(fs2);
};
} else {
return this.sii.annotationFsComparatorWithoutId = (fsx1, fsx2) -> {
if (fsx1 == fsx2) return 0;
Annotation fs1 = (Annotation) fsx1;
Annotation fs2 = (Annotation) fsx2;
return fs1.compareAnnotation(fs2, lto);
};
}
}
// public boolean isAnnotationComparator_usesTypeOrder() {
// final LinearTypeOrder lto = getDefaultTypeOrder();
// return V2_ANNOTATION_COMPARE_TYPE_ORDER || !lto.isEmptyTypeOrder();
// }
//unrolled because of high frequency use
private Comparator<TOP> createAnnotationFsComparatorWithId() {
final LinearTypeOrder lto = getDefaultTypeOrder(); // used as constant in comparator
if (!V2_ANNOTATION_COMPARE_TYPE_ORDER && lto.isEmptyTypeOrder()) {
this.sii.annotationFsComparatorWithId = (fsx1, fsx2) -> {
if (fsx1 == fsx2) return 0;
final Annotation fs1 = (Annotation) fsx1;
final Annotation fs2 = (Annotation) fsx2;
return fs1.compareAnnotationWithId(fs2);
};
} else {
this.sii.annotationFsComparatorWithId = (fsx1, fsx2) -> {
if (fsx1 == fsx2) return 0;
final Annotation fs1 = (Annotation) fsx1;
final Annotation fs2 = (Annotation) fsx2;
return fs1.compareAnnotationWithId(fs2, lto);
};
}
return this.sii.annotationFsComparatorWithId;
}
/**
* Get the FsIndex_iicp for a given typeCode, indexingStrategy, and comparator (type ignored)
* @param typeCode -
* @param indexingStrategy -
* @param comp -
* @param <T> type of Feature Structure
* @return -
*/
public <T extends FeatureStructure> FsIndex_iicp<T> getIndexBySpec(int typeCode, int indexingStrategy, FSIndexComparatorImpl comp) {
return getIndexesForType(typeCode).getIndexExcludingType(indexingStrategy, comp);
}
private void removeIndexBySpec(int typeCode, int indexingStrategy, FSIndexComparatorImpl comp) {
getIndexesForType(typeCode).removeIndexExcludingType(indexingStrategy, comp);
}
public TypeSystemImpl getTypeSystemImpl() {
return sii.tsi;
}
public CASImpl getCasImpl() {
return cas;
}
private Comparator<TOP> getCachedComparator(FSComparators withId, FSComparators withTypeOrder) {
if (withId == FSComparators.WITH_ID) {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
return this.sii.annotationFsComparatorWithId;
} else {
return this.sii.annotationFsComparatorNoTypeWithId;
}
} else {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
return this.sii.annotationFsComparatorWithoutId;
} else {
return this.sii.annotationFsComparatorNoTypeWithoutId;
}
}
}
private void setCachedComparator(FSComparators withId, FSComparators withTypeOrder, Comparator<TOP> c) {
if (withId == FSComparators.WITH_ID) {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
this.sii.annotationFsComparatorWithId = c;
} else {
this.sii.annotationFsComparatorNoTypeWithId = c;
}
} else {
if (withTypeOrder == FSComparators.WITH_TYPE_ORDER) {
this.sii.annotationFsComparatorWithoutId = c;
} else {
this.sii.annotationFsComparatorNoTypeWithoutId = c;
}
}
}
}