blob: c82e3c9dbaba21c62e2cd3296f4ff0af62aa016a [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.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.apache.uima.UIMAFramework;
import org.apache.uima.UIMARuntimeException;
import org.apache.uima.UimaSerializable;
import org.apache.uima.cas.AbstractCas_ImplBase;
import org.apache.uima.cas.ArrayFS;
import org.apache.uima.cas.BooleanArrayFS;
import org.apache.uima.cas.ByteArrayFS;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.CASException;
import org.apache.uima.cas.CASRuntimeException;
import org.apache.uima.cas.CasOwner;
import org.apache.uima.cas.CommonArrayFS;
import org.apache.uima.cas.ComponentInfo;
import org.apache.uima.cas.ConstraintFactory;
import org.apache.uima.cas.DoubleArrayFS;
import org.apache.uima.cas.FSIndex;
import org.apache.uima.cas.FSIndexRepository;
import org.apache.uima.cas.FSIterator;
import org.apache.uima.cas.FSMatchConstraint;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.FeaturePath;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.FeatureValuePath;
import org.apache.uima.cas.FloatArrayFS;
import org.apache.uima.cas.IntArrayFS;
import org.apache.uima.cas.LongArrayFS;
import org.apache.uima.cas.Marker;
import org.apache.uima.cas.SerialFormat;
import org.apache.uima.cas.ShortArrayFS;
import org.apache.uima.cas.SofaFS;
import org.apache.uima.cas.SofaID;
import org.apache.uima.cas.StringArrayFS;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.admin.CASAdminException;
import org.apache.uima.cas.admin.CASFactory;
import org.apache.uima.cas.admin.CASMgr;
import org.apache.uima.cas.admin.FSIndexComparator;
import org.apache.uima.cas.admin.FSIndexRepositoryMgr;
import org.apache.uima.cas.admin.TypeSystemMgr;
import org.apache.uima.cas.impl.FSsTobeAddedback.FSsTobeAddedbackSingle;
import org.apache.uima.cas.impl.SlotKinds.SlotKind;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.cas.text.AnnotationIndex;
import org.apache.uima.cas.text.Language;
import org.apache.uima.internal.util.IntVector;
import org.apache.uima.internal.util.Misc;
import org.apache.uima.internal.util.PositiveIntSet;
import org.apache.uima.internal.util.PositiveIntSet_impl;
import org.apache.uima.jcas.JCas;
import org.apache.uima.jcas.cas.AnnotationBase;
import org.apache.uima.jcas.cas.BooleanArray;
import org.apache.uima.jcas.cas.ByteArray;
import org.apache.uima.jcas.cas.DoubleArray;
import org.apache.uima.jcas.cas.EmptyFSList;
import org.apache.uima.jcas.cas.EmptyFloatList;
import org.apache.uima.jcas.cas.EmptyIntegerList;
import org.apache.uima.jcas.cas.EmptyList;
import org.apache.uima.jcas.cas.EmptyStringList;
import org.apache.uima.jcas.cas.FSArray;
import org.apache.uima.jcas.cas.FloatArray;
import org.apache.uima.jcas.cas.IntegerArray;
import org.apache.uima.jcas.cas.LongArray;
import org.apache.uima.jcas.cas.ShortArray;
import org.apache.uima.jcas.cas.Sofa;
import org.apache.uima.jcas.cas.StringArray;
import org.apache.uima.jcas.cas.TOP;
import org.apache.uima.jcas.impl.JCasHashMap;
import org.apache.uima.jcas.impl.JCasImpl;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.uima.util.Level;
/**
* Implements the CAS interfaces. This class must be public because we need to
* be able to create instance of it from outside the package. Use at your own
* risk. May change without notice.
*
*/
public class CASImpl extends AbstractCas_ImplBase implements CAS, CASMgr, LowLevelCAS, TypeSystemConstants {
public static final boolean IS_USE_V2_IDS = false; // if false, ids increment by 1
private static final boolean trace = false; // debug
public static final boolean traceFSs = false; // debug - trace FS creation and update
public static final boolean traceCow = false; // debug - trace copy on write actions, index adds / deletes
private static final String traceFile = "traceFSs.log.txt";
private static final PrintStream traceOut;
static {
try {
if (traceFSs) {
System.out.println("Creating traceFSs file in directory " + System.getProperty("user.dir"));
traceOut = traceFSs ? new PrintStream(new BufferedOutputStream(new FileOutputStream(traceFile, false))) : null;
} else {
traceOut = null;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final boolean MEASURE_SETINT = false;
// debug
static final AtomicInteger casIdProvider = new AtomicInteger(0);
// Notes on the implementation
// ---------------------------
// Floats are handled by casting them to ints when they are stored
// in the heap. Conveniently, 0 casts to 0.0f, which is the default
// value.
public static final int NULL = 0;
// Boolean scalar values are stored as ints in the fs heap.
// TRUE is 1 and false is 0.
public static final int TRUE = 1;
public static final int FALSE = 0;
public static final int DEFAULT_INITIAL_HEAP_SIZE = 500_000;
public static final int DEFAULT_RESET_HEAP_SIZE = 5_000_000;
/**
* The UIMA framework detects (unless disabled, for high performance) updates to indexed FS which update
* key values used as keys in indexes. Normally the framework will protect against index corruption by
* temporarily removing the FS from the indexes, then do the update to the feature value, and then addback
* the changed FS.
* <p>
* Users can use the protectIndexes() methods to explicitly control this remove - add back cycle, for instance
* to "batch" together several updates to multiple features in a FS.
* <p>
* Some build processes may want to FAIL if any unprotected updates of this kind occur, instead of having the
* framework silently recover them. This is enabled by having the framework throw an exception; this is controlled
* by this global JVM property, which, if defined, causes the framework to throw an exception rather than recover.
*
*/
public static final String THROW_EXCEPTION_FS_UPDATES_CORRUPTS = "uima.exception_when_fs_update_corrupts_index";
// public for test case use
public static boolean IS_THROW_EXCEPTION_CORRUPT_INDEX = Misc.getNoValueSystemProperty(THROW_EXCEPTION_FS_UPDATES_CORRUPTS);
/**
* Define this JVM property to enable checking for invalid updates to features which are used as
* keys by any index.
* <ul>
* <li>The following are the same: -Duima.check_invalid_fs_updates and -Duima.check_invalid_fs_updates=true</li>
* </ul>
*/
public static final String REPORT_FS_UPDATES_CORRUPTS = "uima.report_fs_update_corrupts_index";
private static final boolean IS_REPORT_FS_UPDATE_CORRUPTS_INDEX =
IS_THROW_EXCEPTION_CORRUPT_INDEX || Misc.getNoValueSystemProperty(REPORT_FS_UPDATES_CORRUPTS);
/**
* Set this JVM property to false for high performance, (no checking);
* insure you don't have the report flag (above) turned on - otherwise it will force this to "true".
*/
public static final String DISABLE_PROTECT_INDEXES = "uima.disable_auto_protect_indexes";
/**
* the protect indexes flag is on by default, but may be turned of via setting the property.
*
* This is overridden if a report is requested or the exception detection is on.
*/
private static final boolean IS_DISABLED_PROTECT_INDEXES =
Misc.getNoValueSystemProperty(DISABLE_PROTECT_INDEXES) &&
!IS_REPORT_FS_UPDATE_CORRUPTS_INDEX &&
!IS_THROW_EXCEPTION_CORRUPT_INDEX;
public static final String ALWAYS_HOLD_ONTO_FSS = "uima.enable_id_to_feature_structure_map_for_all_fss";
static final boolean IS_ALWAYS_HOLD_ONTO_FSS = // debug
Misc.getNoValueSystemProperty(ALWAYS_HOLD_ONTO_FSS);
// private static final int REF_DATA_FOR_ALLOC_SIZE = 1024;
// private static final int INT_DATA_FOR_ALLOC_SIZE = 1024;
//
// this next seemingly non-sensical static block
// is to force the classes needed by Eclipse debugging to load
// otherwise, you get a com.sun.jdi.ClassNotLoadedException when
// the class is used as part of formatting debugging messages
static {
new DebugNameValuePair(null, null);
new DebugFSLogicalStructure();
}
// Static classes representing shared instance data
// - shared data is computed once for all views
/**
* Journaling changes for computing delta cas.
* Each instance represents one or more changes for one feature structure
* A particular Feature Structure may have multiple FsChange instances
* but we attempt to minimize this
*/
public static class FsChange {
/** ref to the FS being modified */
final TOP fs;
/**
* which feature (by offset) is modified
*/
final BitSet featuresModified;
final PositiveIntSet arrayUpdates;
FsChange(TOP fs) {
this.fs = fs;
TypeImpl ti = fs._getTypeImpl();
featuresModified = (ti.highestOffset == -1) ? null : new BitSet(ti.highestOffset + 1);
arrayUpdates = (ti.isArray()) ? new PositiveIntSet_impl() : null;
}
void addFeatData(int v) {
featuresModified.set(v);
}
void addArrayData(int v, int nbrOfConsecutive) {
for (int i = 0; i < nbrOfConsecutive; i++) {
arrayUpdates.add(v++);
}
}
void addArrayData(PositiveIntSet indexesPlus1) {
indexesPlus1.forAllInts(i -> arrayUpdates.add(i - 1));
}
@Override
public int hashCode() {
return 31 + ((fs == null) ? 0 : fs._id);
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof FsChange))
return false;
return ((FsChange)obj).fs._id == fs._id;
}
}
// fields shared among all CASes belong to views of a common base CAS
static class SharedViewData {
/**
* map from FS ids to FSs.
*/
final private Id2FS id2fs;
/** set to > 0 to reuse an id, 0 otherwise */
private int reuseId = 0;
// Base CAS for all views
final private CASImpl baseCAS;
/**
* These fields are here, not in TypeSystemImpl, because different CASes may have different indexes but share the same type system
* They hold the same data (constant per CAS) but are accessed with different indexes
*/
private final BitSet featureCodesInIndexKeys = new BitSet(1024); // 128 bytes
// private final BitSet featureJiInIndexKeys = new BitSet(1024); // indexed by JCas Feature Index, not feature code.
// A map from SofaNumbers which are also view numbers to IndexRepositories.
// these numbers are dense, and start with 1. 1 is the initial view. 0 is the base cas
ArrayList<FSIndexRepositoryImpl> sofa2indexMap;
/**
* A map from Sofa numbers to CAS views.
* number 0 - not used
* number 1 - used for view named "_InitialView"
* number 2-n used for other views
*
* Note: this is not reset with "Cas Reset" because views (really, their associated index repos)
* take a lot of setup for the indexes.
* However, the maximum view count is reset; so creation of new views "reuses" these pre-setup indexRepos
* associated with these views.
*/
ArrayList<CASImpl> sofaNbr2ViewMap;
/**
* a set of instantiated sofaNames
*/
private Set<String> sofaNameSet;
// Flag that initial Sofa has been created
private boolean initialSofaCreated = false;
// Count of Views created in this cas
// equals count of sofas except if initial view has no sofa.
int viewCount;
// The ClassLoader that should be used by the JCas to load the generated
// FS cover classes for this CAS. Defaults to the ClassLoader used
// to load the CASImpl class.
private ClassLoader jcasClassLoader = this.getClass().getClassLoader();
/*****************************
* PEAR Support
*****************************/
/**
* Only support one level of PEAR nesting; for more general approach, make this a deque
*/
private ClassLoader previousJCasClassLoader = null;
/**
* Save area for suspending this while we create a base instance
*/
private ClassLoader suspendPreviousJCasClassLoader;
/**
* A map from IDs to already created trampoline FSs for the base FS with that id.
* These are used when in a Pear and retrieving a FS (via index or deref) and you want the
* Pear version for that ID.
* There are potentially multiple maps - one per PEAR Classpath
*/
private JCasHashMap id2tramp = null;
/**
* a map from IDs of FSs that have a Pear version, to the base (non-Pear) version
* used to locate the base version for adding to indexes
*/
private JCasHashMap id2base = null;
private final Map<ClassLoader, JCasHashMap> cl2id2tramp = new IdentityHashMap<>();
/**
* The current (active, switches at Pear boundaries) FsGenerators (excluding array-generators)
* key = type code
* read-only, unsynchronized for this CAS
* Cache for setting this kept in TypeSystemImpl, by classloader
* - shared among all CASs that use that Type System and class loader
* -- in turn, initialized from FSClassRegistry, once per classloader / typesystem combo
*
* Pear generators are mostly null except for instances where the PEAR has redefined
* the JCas cover class
*/
private FsGenerator3[] generators;
/**
* When generating a new instance of a FS in a PEAR where there's an alternate JCas class impl,
* generate the base version, and make the alternate a trampoline to it.
* Note: in future, if it is known that this FS is never used outside of this PEAR, then can
* skip generating the double version
*/
private FsGenerator3[] baseGenerators;
// If this CAS can be flushed (reset) or not.
// often, the framework disables this before calling users code
private boolean flushEnabled = true;
// not final because set with reinit deserialization
private TypeSystemImpl tsi;
private ComponentInfo componentInfo;
/**
* This tracks the changes for delta cas
* May also in the future support Journaling by component,
* allowing determination of which component in a flow
* created/updated a FeatureStructure (not implmented)
*
* TrackingMarkers are held on to by things outside of the
* Cas, to support switching from one tracking marker to
* another (currently not used, but designed to support
* Component Journaling).
*
* We track changes on a granularity of features
* and for features which are arrays, which element of the array
* (This last to enable efficient delta serializations of
* giant arrays of things, where you've only updated a few items)
*
* The FsChange doesn't store the changed data, only stores the
* ref info needed to get to what was changed.
*/
private MarkerImpl trackingMark;
/**
* Track modified preexistingFSs
* Note this is a map, keyed by the FS, so all changes are merged when added
*/
private Map<TOP, FsChange> modifiedPreexistingFSs;
/**
* This list currently only contains at most 1 element.
* If Journaling is implemented, it may contain an
* element per component being journaled.
*/
private List<MarkerImpl> trackingMarkList;
/**
* This stack corresponds to nested protectIndexes contexts. Normally should be very shallow.
*/
private final ArrayList<FSsTobeAddedback> fssTobeAddedback = new ArrayList<FSsTobeAddedback>();
/**
* This version is for single fs use, by binary deserializers and by automatic mode
* Only one user at a time is allowed.
*/
private final FSsTobeAddedbackSingle fsTobeAddedbackSingle = (FSsTobeAddedbackSingle) FSsTobeAddedback.createSingle();
/**
* Set to true while this is in use.
*/
boolean fsTobeAddedbackSingleInUse = false;
/**
* temporarily set to true by deserialization routines doing their own management of this check
*/
boolean disableAutoCorruptionCheck = false;
// used to generate FSIDs, increments by 1 for each use. First id == 1
/**
* The fsId of the last created FS
* used to generate FSIDs, increments by 1 for each use. First id == 1
*/
private int fsIdGenerator = 0;
/**
* The version 2 size on the main heap of the last created FS
*/
private int lastFsV2Size = 1;
/**
* used to "capture" the fsIdGenerator value for a read-only CAS to be visible in
* other threads
*/
AtomicInteger fsIdLastValue = new AtomicInteger(0);
// mostly for debug - counts # times cas is reset
private final AtomicInteger casResets = new AtomicInteger(0);
// unique ID for a created CAS view, not updated if CAS is reset and reused
private final int casId = casIdProvider.incrementAndGet();
// shared singltons, created at type system commit
private EmptyFSList emptyFSList;
private EmptyFloatList emptyFloatList;
private EmptyIntegerList emptyIntegerList;
private EmptyStringList emptyStringList;
/**
* Created at startup time, lives as long as the CAS lives
* Serves to reference code for binary cas ser/des that used to live
* in this class, but was moved out
*/
private final BinaryCasSerDes bcsd;
/**
* Created when doing binary or form4 non-delta (de)serialization, used in subsequent delta ser/deserialization
* Created when doing binary or form4 non-delta ser/deserialization, used in subsequent delta (de)serialization
* Reset with CasReset or deltaMergesComplete API call
*/
private CommonSerDesSequential csds;
/*************************************************
* VERSION 2 LOW_LEVEL_API COMPATIBILITY SUPPORT *
*************************************************/
/**
* A StringSet used only to support ll_get/setInt api
* get adds string to this and returns the int handle
* set retrieves the string, given the handle
* lazy initialized
*/
private StringSet llstringSet = null;
/**
* A LongSet used only to support v2 ll_get/setInt api
* get adds long to this and returns the int handle
* set retrieves the long, given the handle
* lazy initialized
*/
private LongSet lllongSet = null;
// For tracing FS creation and updating, normally disabled
private final StringBuilder traceFScreationSb = traceFSs ? new StringBuilder() : null;
private final StringBuilder traceCowSb = traceCow ? new StringBuilder() : null;
private int traceFSid = 0;
private boolean traceFSisCreate;
private final IntVector id2addr = traceFSs ? new IntVector() : null;
private int nextId2Addr = 1; // only for tracing, to convert id's to v2 addresses
final private int initialHeapSize;
private SharedViewData(CASImpl baseCAS, int initialHeapSize, TypeSystemImpl tsi) {
this.baseCAS = baseCAS;
this.tsi = tsi;
this.initialHeapSize = initialHeapSize;
bcsd = new BinaryCasSerDes(baseCAS);
id2fs = new Id2FS(initialHeapSize);
if (traceFSs) id2addr.add(0);
}
void clearCasReset() {
// fss
fsIdGenerator = 0;
id2fs.clear();
// pear caches
id2tramp = null;
id2base = null;
for (JCasHashMap m : cl2id2tramp.values()) {
m.clear();
}
// index corruption avoidance
fssTobeAddedback.clear();
fsTobeAddedbackSingle.clear();
fsTobeAddedbackSingleInUse = false;
disableAutoCorruptionCheck = false;
// misc
flushEnabled = true;
componentInfo = null;
bcsd.clear();
csds = null;
llstringSet = null;
traceFSid = 0;
if (traceFSs) {
traceFScreationSb.setLength(0);
id2addr.removeAllElements();
id2addr.add(0);
nextId2Addr = 1;
}
}
/**
* called by resetNoQuestions and cas complete reinit
*/
void clearSofaInfo() {
sofaNameSet.clear();
initialSofaCreated = false;
}
/**
* Called from CasComplete deserialization (reinit).
*
* Skips the resetNoQuestions operation of flushing the indexes,
* since these will be reinitialized with potentially new definitions.
*
* Clears additional data related to having the
* - type system potentially change
* - the features belonging to indexes change
*
*/
void clear() {
clearCasReset();
clearTrackingMarks();
// type system + index spec
tsi = null;
featureCodesInIndexKeys.clear();
// featureJiInIndexKeys.clear();
emptyFloatList = null; // these cleared in case new ts redefines?
emptyFSList = null;
emptyIntegerList = null;
emptyStringList = null;
clearSofaInfo();
/**
* Clear the existing views, except keep the info for the initial view
* so that the cas complete deserialization after setting up the new index repository in the base cas
* can "refresh" the existing initial view (if present; if not present, a new one is created).
*/
if (sofaNbr2ViewMap.size() >= 1) {
// have initial view - preserve it
CASImpl localInitialView = sofaNbr2ViewMap.get(1);
sofaNbr2ViewMap.clear();
Misc.setWithExpand(sofaNbr2ViewMap, 1, localInitialView);
viewCount = 1;
} else {
sofaNbr2ViewMap.clear();
viewCount = 0;
}
}
private void clearTrackingMarks() {
// resets all markers that might be held by things outside the Cas
// Currently (2009) this list has a max of 1 element
// Future impl may have one element per component for component Journaling
if (trackingMarkList != null) {
for (int i=0; i < trackingMarkList.size(); i++) {
trackingMarkList.get(i).isValid = false;
}
}
trackingMark = null;
if (null != modifiedPreexistingFSs) {
modifiedPreexistingFSs.clear();
}
trackingMarkList = null;
}
void switchClassLoader(ClassLoader newClassLoader) {
if (null == newClassLoader) { // is null if no cl set
return;
}
if (newClassLoader != jcasClassLoader) {
if (null != previousJCasClassLoader) {
/** Multiply nested classloaders not supported. Original base loader: {0}, current nested loader: {1}, trying to switch to loader: {2}.*/
throw new CASRuntimeException(CASRuntimeException.SWITCH_CLASS_LOADER_NESTED,
previousJCasClassLoader, jcasClassLoader, newClassLoader);
}
// System.out.println("Switching to new class loader");
previousJCasClassLoader = jcasClassLoader;
jcasClassLoader = newClassLoader;
generators = tsi.getGeneratorsForClassLoader(newClassLoader, true); // true - isPear
assert null == id2tramp; // is null outside of a pear
id2tramp = cl2id2tramp.get(newClassLoader);
if (null == id2tramp) {
cl2id2tramp.put(newClassLoader, id2tramp = new JCasHashMap(32));
}
if (id2base == null) {
id2base = new JCasHashMap(32);
}
}
}
void restoreClassLoader() {
if (null == previousJCasClassLoader) {
return;
}
// System.out.println("Switching back to previous class loader");
jcasClassLoader = previousJCasClassLoader;
previousJCasClassLoader = null;
generators = baseGenerators;
id2tramp = null;
}
private int getNextFsId(TOP fs) {
if (reuseId != 0) {
// l.setStrongRef(fs, reuseId);
return reuseId;
}
// l.add(fs);
// if (id2fs.size() != (2 + fsIdGenerator.get())) {
// System.out.println("debug out of sync id generator and id2fs size");
// }
// assert(l.size() == (2 + fsIdGenerator));
final int p = fsIdGenerator;
final int r = fsIdGenerator += IS_USE_V2_IDS
? lastFsV2Size
: 1;
if (r < p) {
throw new RuntimeException("UIMA Cas Internal id value overflowed maximum int value");
}
if (IS_USE_V2_IDS) {
// this computation is partial - misses length of arrays stored on heap
// because that info not yet available
lastFsV2Size = fs._getTypeImpl().getFsSpaceReq();
}
return r;
}
}
/*****************************************************************
* Non-shared instance data kept per CAS view incl base CAS
*****************************************************************/
// package protected to let other things share this info
final SharedViewData svd; // shared view data
/** The index repository. Referenced by XmiCasSerializer */
FSIndexRepositoryImpl indexRepository;
// private Object[] currentRefDataForAlloc = new Object[REF_DATA_FOR_ALLOC_SIZE];
// private Object[] returnRefDataForAlloc;
// private int[] currentIntDataForAlloc = new int[INT_DATA_FOR_ALLOC_SIZE];
// private int[] returnIntDataForAlloc;
// private int nextRefDataOffsetForAlloc = 0;
// private int nextIntDataOffsetForAlloc = 0;
/**
* The Feature Structure for the sofa FS for this view, or
* null
* //-1 if the sofa FS is for the initial view, or
* // 0 if there is no sofa FS - for instance, in the "base cas"
*/
private Sofa mySofaRef = null;
/** the corresponding JCas object */
JCasImpl jcas = null;
/**
* Copies of frequently accessed data pulled up for
* locality of reference - only an optimization
* - each value needs to be reset appropriately
* - getters check for null, and if null, do the get.
*/
private TypeSystemImpl tsi_local;
/**
* for Pear generation - set this to the base FS
* not in SharedViewData to reduce object traversal when
* generating FSs
*/
FeatureStructureImplC pearBaseFs = null;
// private StackTraceElement[] addbackSingleTrace = null; // for debug use only, normally commented out
// CASImpl(TypeSystemImpl typeSystem) {
// this(typeSystem, DEFAULT_INITIAL_HEAP_SIZE);
// }
// // Reference existing CAS
// // For use when creating views of the CAS
// CASImpl(CAS cas) {
// this.setCAS(cas);
// this.useFSCache = false;
// initTypeVariables();
// }
/*
* Configure a new (base view) CASImpl, **not a new view** typeSystem can be
* null, in which case a new instance of TypeSystemImpl is set up, but not
* committed. If typeSystem is not null, it is committed (locked). ** Note: it
* is assumed that the caller of this will always set up the initial view **
* by calling
*/
public CASImpl(TypeSystemImpl typeSystem, int initialHeapSize ) {
super();
TypeSystemImpl ts;
final boolean externalTypeSystem = (typeSystem != null);
if (externalTypeSystem) {
ts = typeSystem;
} else {
ts = (TypeSystemImpl) CASFactory.createTypeSystem(); // creates also new CASMetadata and
// FSClassRegistry instances
}
this.svd = new SharedViewData(this, initialHeapSize, ts);
// this.svd.baseCAS = this;
// this.svd.heap = new Heap(initialHeapSize);
if (externalTypeSystem) {
commitTypeSystem();
}
this.svd.sofa2indexMap = new ArrayList<>();
this.svd.sofaNbr2ViewMap = new ArrayList<>();
this.svd.sofaNameSet = new HashSet<String>();
this.svd.initialSofaCreated = false;
this.svd.viewCount = 0;
this.svd.clearTrackingMarks();
}
public CASImpl() {
this((TypeSystemImpl) null, DEFAULT_INITIAL_HEAP_SIZE);
}
// In May 2007, appears to have 1 caller, createCASMgr in Serialization class,
// could have out-side the framework callers because it is public.
public CASImpl(CASMgrSerializer ser) {
this(ser.getTypeSystem(), DEFAULT_INITIAL_HEAP_SIZE);
checkInternalCodes(ser);
// assert(ts != null);
// assert(getTypeSystem() != null);
this.indexRepository = ser.getIndexRepository(this);
}
// Use this when creating a CAS view
CASImpl(CASImpl cas, SofaFS aSofa) {
// these next fields are final and must be set in the constructor
this.svd = cas.svd;
this.mySofaRef = (Sofa) aSofa;
// get the indexRepository for this Sofa
this.indexRepository = (this.mySofaRef == null) ?
(FSIndexRepositoryImpl) cas.getSofaIndexRepository(1) :
(FSIndexRepositoryImpl) cas.getSofaIndexRepository(aSofa);
if (null == this.indexRepository) {
// create the indexRepository for this CAS
// use the baseIR to create a lightweight IR copy
FSIndexRepositoryImpl baseIndexRepo = (FSIndexRepositoryImpl) cas.getBaseIndexRepository();
this.indexRepository = new FSIndexRepositoryImpl(this, baseIndexRepo);
// the index creation depends on "indexRepository" already being set
baseIndexRepo.name2indexMap.keySet().stream().forEach(key -> this.indexRepository.createIndex(baseIndexRepo, key));
this.indexRepository.commit();
// save new sofa index
if (this.mySofaRef == null) {
cas.setSofaIndexRepository(1, this.indexRepository);
} else {
cas.setSofaIndexRepository(aSofa, this.indexRepository);
}
}
}
// Use this when creating a CAS view
void refreshView(CAS cas, SofaFS aSofa) {
if (aSofa != null) {
// save address of SofaFS
this.mySofaRef = (Sofa) aSofa;
} else {
// this is the InitialView
this.mySofaRef = null;
}
// toss the JCas, if it exists
this.jcas = null;
// create the indexRepository for this Sofa
final FSIndexRepositoryImpl baseIndexRepo = (FSIndexRepositoryImpl) ((CASImpl) cas).getBaseIndexRepository();
this.indexRepository = new FSIndexRepositoryImpl(this,baseIndexRepo);
// the index creation depends on "indexRepository" already being set
baseIndexRepo.name2indexMap.keySet().stream().forEach(key -> this.indexRepository.createIndex(baseIndexRepo, key));
this.indexRepository.commit();
// save new sofa index
if (this.mySofaRef == null) {
((CASImpl) cas).setSofaIndexRepository(1, this.indexRepository);
} else {
((CASImpl) cas).setSofaIndexRepository(aSofa, this.indexRepository);
}
}
private void checkInternalCodes(CASMgrSerializer ser) throws CASAdminException {
if ((ser.topTypeCode > 0)
&& (ser.topTypeCode != topTypeCode)) {
throw new CASAdminException(CASAdminException.DESERIALIZATION_ERROR);
}
if (ser.featureOffsets == null) {
return;
}
// if (ser.featureOffsets.length != this.svd.casMetadata.featureOffset.length) {
// throw new CASAdminException(CASAdminException.DESERIALIZATION_ERROR);
// }
TypeSystemImpl tsi = getTypeSystemImpl();
for (int i = 1; i < ser.featureOffsets.length; i++) {
FeatureImpl fi = tsi.getFeatureForCode_checked(i);
int adjOffset = fi.isInInt ? 0 : fi.getRangeImpl().nbrOfUsedIntDataSlots;
if (ser.featureOffsets[i] != (fi.getOffset() + adjOffset)) {
throw new CASAdminException(CASAdminException.DESERIALIZATION_ERROR);
}
}
}
// ----------------------------------------
// accessors for data in SharedViewData
// ----------------------------------------
void addSofaViewName(String id) {
svd.sofaNameSet.add(id);
}
void setViewCount(int n) {
svd.viewCount = n;
}
void addbackSingle(TOP fs) {
if (!svd.fsTobeAddedbackSingleInUse) {
Misc.internalError();
}
svd.fsTobeAddedbackSingle.addback(fs);
svd.fsTobeAddedbackSingleInUse = false;
}
void addbackSingleIfWasRemoved(boolean wasRemoved, TOP fs) {
if (wasRemoved) {
addbackSingle(fs);
}
svd.fsTobeAddedbackSingleInUse = false;
}
private FSsTobeAddedback getAddback(int size) {
if (svd.fsTobeAddedbackSingleInUse) {
Misc.internalError();
}
return svd.fssTobeAddedback.get(size - 1);
}
FSsTobeAddedbackSingle getAddbackSingle() {
if (svd.fsTobeAddedbackSingleInUse) {
// System.out.println(Misc.dumpCallers(addbackSingleTrace, 2, 100));
Misc.internalError();
}
// addbackSingleTrace = Thread.currentThread().getStackTrace();
svd.fsTobeAddedbackSingleInUse = true;
svd.fsTobeAddedbackSingle.clear(); // safety
return svd.fsTobeAddedbackSingle;
}
void featureCodes_inIndexKeysAdd(int featCode/*, int registryIndex*/) {
svd.featureCodesInIndexKeys.set(featCode);
// skip adding if no JCas registry entry for this feature
// if (registryIndex >= 0) {
// svd.featureJiInIndexKeys.set(registryIndex);
// }
}
@Override
public void enableReset(boolean flag) {
this.svd.flushEnabled = flag;
}
@Override
public TypeSystem getTypeSystem() {
return getTypeSystemImpl();
}
public TypeSystemImpl getTypeSystemImpl() {
if (tsi_local == null) {
tsi_local = this.svd.tsi;
}
return this.tsi_local;
}
/**
* Set the shared svd type system ref, in all views
* @param ts
*/
void installTypeSystemInAllViews(TypeSystemImpl ts) {
this.svd.tsi = ts;
final List<CASImpl> sn2v = this.svd.sofaNbr2ViewMap;
if (sn2v.size() > 0) {
for (CASImpl view : sn2v.subList(1, sn2v.size())) {
view.tsi_local = ts;
}
}
this.getBaseCAS().tsi_local = ts;
}
@Override
public ConstraintFactory getConstraintFactory() {
return ConstraintFactory.instance();
}
/**
* Create the appropriate Feature Structure Java instance
* - from whatever the generator for this type specifies.
*
* @param type the type to create
* @return a Java object representing the FeatureStructure impl in Java.
*/
@Override
public <T extends FeatureStructure> T createFS(Type type) {
final TypeImpl ti = (TypeImpl) type;
if (!ti.isCreatableAndNotBuiltinArray()) {
throw new CASRuntimeException(CASRuntimeException.NON_CREATABLE_TYPE, type.getName(), "CAS.createFS()");
}
return (T) createFSAnnotCheck(ti);
}
private <T extends FeatureStructureImplC> T createFSAnnotCheck(TypeImpl ti) {
if (ti.isAnnotationBaseType()) {
// not here, will be checked later in AnnotationBase constructor
// if (this.isBaseCas()) {
// throw new CASRuntimeException(CASRuntimeException.DISALLOW_CREATE_ANNOTATION_IN_BASE_CAS, ti.getName());
// }
getSofaRef(); // materialize this if not present; required for setting the sofa ref
// must happen before the annotation is created, for compressed form 6 serialization order
// to insure sofa precedes the ref of it
}
FsGenerator3 g = svd.generators[ti.getCode()]; // get generator or null
return (g != null)
? (T) g.createFS(ti, this)
// pear case, with no overriding pear - use base
: (T) createFsFromGenerator(svd.baseGenerators, ti);
//
//
// // not pear or no special cover class for this
//
//
// TOP fs = createFsFromGenerator(svd.baseGenerators, ti);
//
// FsGenerator g = svd.generators[ti.getCode()]; // get pear generator or null
// return (g != null)
// ? (T) pearConvert(fs, g)
// : (T) fs;
// }
//
// return (T) createFsFromGenerator(svd.generators, ti);
}
/**
* Called during construction of FS.
* For normal FS "new" operators, if in PEAR context, make the base version
* @param fs
* @param ti
* @return true if made a base for a trampoline
*/
boolean maybeMakeBaseVersionForPear(FeatureStructureImplC fs, TypeImpl ti) {
if (!inPearContext()) return false;
FsGenerator3 g = svd.generators[ti.getCode()]; // get pear generator or null
if (g == null) return false;
TOP baseFs;
try {
suspendPearContext();
svd.reuseId = fs._id;
baseFs = createFsFromGenerator(svd.baseGenerators, ti);
} finally {
restorePearContext();
svd.reuseId = 0;
}
svd.id2base.put(baseFs);
pearBaseFs = baseFs;
return true;
}
private TOP createFsFromGenerator(FsGenerator3[] gs, TypeImpl ti) {
// if (ti == null || gs == null || gs[ti.getCode()] == null) {
// System.out.println("debug");
// }
return gs[ti.getCode()].createFS(ti, this);
}
// public int ll_createFSAnnotCheck(int typeCode) {
// TOP fs = createFSAnnotCheck(getTypeFromCode(typeCode));
// svd.id2fs.put(fs); // required for low level, in order to "hold onto" / prevent GC of FS
// return fs._id;
// }
public TOP createArray(TypeImpl array_type, int arrayLength) {
TypeImpl_array tia = (TypeImpl_array) array_type;
TypeImpl componentType = tia.getComponentType();
if (componentType.isPrimitive()) {
checkArrayPreconditions(arrayLength);
switch (componentType.getCode()) {
case intTypeCode: return new IntegerArray(array_type, this, arrayLength);
case floatTypeCode: return new FloatArray(array_type, this, arrayLength);
case booleanTypeCode: return new BooleanArray(array_type, this, arrayLength);
case byteTypeCode: return new ByteArray(array_type, this, arrayLength);
case shortTypeCode: return new ShortArray(array_type, this, arrayLength);
case longTypeCode: return new LongArray(array_type, this, arrayLength);
case doubleTypeCode: return new DoubleArray(array_type, this, arrayLength);
case stringTypeCode: return new StringArray(array_type, this, arrayLength);
// case javaObjectTypeCode: return new JavaObjectArray(array_type, this, arrayLength);
default: Misc.internalError();
}
// return tia.getGeneratorArray().createFS(type, this, arrayLength);
// return (((FsGeneratorArray)getFsGenerator(type.getCode())).createFS(type, this, arrayLength));
}
return (TOP) createArrayFS(array_type, arrayLength);
}
@Override
public ArrayFS createArrayFS(int length) {
return createArrayFS(getTypeSystemImpl().fsArrayType, length);
}
private ArrayFS createArrayFS(TypeImpl type, int length) {
checkArrayPreconditions(length);
return new FSArray(type, this, length);
// getTypeSystemImpl().fsArrayType.getGeneratorArray() // (((FsGeneratorArray)getFsGenerator(fsArrayTypeCode))
// .createFS(type, this, length);
}
@Override
public IntArrayFS createIntArrayFS(int length) {
checkArrayPreconditions(length);
return new IntegerArray(getTypeSystemImpl().intArrayType, this, length);
}
@Override
public FloatArrayFS createFloatArrayFS(int length) {
checkArrayPreconditions(length);
return new FloatArray(getTypeSystemImpl().floatArrayType, this, length);
}
@Override
public StringArrayFS createStringArrayFS(int length) {
checkArrayPreconditions(length);
return new StringArray(getTypeSystemImpl().stringArrayType, this, length);
}
// public JavaObjectArray createJavaObjectArrayFS(int length) {
// checkArrayPreconditions(length);
// return new JavaObjectArray(getTypeSystemImpl().javaObjectArrayType, this, length);
// }
// return true if only one sofa and it is the default text sofa
public boolean isBackwardCompatibleCas() {
// check that there is exactly one sofa
if (this.svd.viewCount != 1) {
return false;
}
if (!this.svd.initialSofaCreated) {
return false;
}
Sofa sofa = this.getInitialView().getSofa();
// check for mime type exactly equal to "text"
String sofaMime = sofa.getMimeType();
if (!"text".equals(sofaMime)) {
return false;
}
// check that sofaURI and sofaArray are not set
String sofaUri = sofa.getSofaURI();
if (sofaUri != null) {
return false;
}
TOP sofaArray = sofa.getSofaArray();
if (sofaArray != null) {
return false;
}
// check that name is NAME_DEFAULT_SOFA
String sofaname = sofa.getSofaID();
return NAME_DEFAULT_SOFA.equals(sofaname);
}
int getViewCount() {
return this.svd.viewCount;
}
FSIndexRepository getSofaIndexRepository(SofaFS aSofa) {
return getSofaIndexRepository(aSofa.getSofaRef());
}
FSIndexRepositoryImpl getSofaIndexRepository(int aSofaRef) {
if (aSofaRef >= this.svd.sofa2indexMap.size()) {
return null;
}
return this.svd.sofa2indexMap.get(aSofaRef);
}
void setSofaIndexRepository(SofaFS aSofa, FSIndexRepositoryImpl indxRepos) {
setSofaIndexRepository(aSofa.getSofaRef(), indxRepos);
}
void setSofaIndexRepository(int aSofaRef, FSIndexRepositoryImpl indxRepos) {
Misc.setWithExpand(this.svd.sofa2indexMap, aSofaRef, indxRepos);
}
/**
* @deprecated
*/
@Override
@Deprecated
public SofaFS createSofa(SofaID sofaID, String mimeType) {
// extract absolute SofaName string from the ID
SofaFS aSofa = createSofa(sofaID.getSofaID(), mimeType);
getView(aSofa); // will create the view, needed to make the
// resetNoQuestions and other things that
// iterate over views work.
return aSofa;
}
Sofa createSofa(String sofaName, String mimeType) {
return createSofa(++this.svd.viewCount, sofaName, mimeType);
}
Sofa createSofa(int sofaNum, String sofaName, String mimeType) {
if (this.svd.sofaNameSet.contains(sofaName)) {
throw new CASRuntimeException(CASRuntimeException.SOFANAME_ALREADY_EXISTS, sofaName);
}
final boolean viewAlreadyExists = sofaNum == this.svd.viewCount;
if (!viewAlreadyExists) {
if (sofaNum == 1) { // skip the test for sofaNum == 1 - this can be set "later"
if (this.svd.viewCount == 0) {
this.svd.viewCount = 1;
} // else it is == or higher, so don't reset it down
} else { // sofa is not initial sofa - is guaranteed to be set when view created
// if (sofaNum != this.svd.viewCount + 1) {
// System.out.println("debug");
// }
assert (sofaNum == this.svd.viewCount + 1);
this.svd.viewCount = sofaNum;
}
}
Sofa sofa = new Sofa(
getTypeSystemImpl().sofaType,
this.getBaseCAS(), // view for a sofa is the base cas to correspond to where it gets indexed
sofaNum,
sofaName,
mimeType);
this.getBaseIndexRepository().addFS(sofa);
this.svd.sofaNameSet.add(sofaName);
if (!viewAlreadyExists) {
getView(sofa); // create the view that goes with this Sofa
}
return sofa;
}
Sofa createInitialSofa(String mimeType) {
Sofa sofa = createSofa(1, CAS.NAME_DEFAULT_SOFA, mimeType);
registerInitialSofa();
this.mySofaRef = sofa;
return sofa;
}
void registerInitialSofa() {
this.svd.initialSofaCreated = true;
}
boolean isInitialSofaCreated() {
return this.svd.initialSofaCreated;
}
/**
* @deprecated
*/
@Override
@Deprecated
public SofaFS getSofa(SofaID sofaID) {
// extract absolute SofaName string from the ID
return getSofa(sofaID.getSofaID());
}
private SofaFS getSofa(String sofaName) {
FSIterator<Sofa> iterator = this.svd.baseCAS.getSofaIterator();
while (iterator.hasNext()) {
SofaFS sofa = iterator.next();
if (sofaName.equals(sofa.getSofaID())) {
return sofa;
}
}
throw new CASRuntimeException(CASRuntimeException.SOFANAME_NOT_FOUND, sofaName);
}
SofaFS getSofa(int sofaRef) {
SofaFS aSofa = (SofaFS) this.ll_getFSForRef(sofaRef);
if (aSofa == null) {
CASRuntimeException e = new CASRuntimeException(CASRuntimeException.SOFAREF_NOT_FOUND);
throw e;
}
return aSofa;
}
public int ll_getSofaNum(int sofaRef) {
return ((Sofa)getFsFromId_checked(sofaRef)).getSofaNum();
}
public String ll_getSofaID(int sofaRef) {
return ((Sofa)getFsFromId_checked(sofaRef)).getSofaID();
}
public String ll_getSofaDataString(int sofaAddr) {
return ((Sofa)getFsFromId_checked(sofaAddr)).getSofaString();
}
public CASImpl getBaseCAS() {
return this.svd.baseCAS;
}
@Override
public <T extends SofaFS> FSIterator<T> getSofaIterator() {
FSIndex<T> sofaIndex = this.svd.baseCAS.indexRepository.<T>getIndex(CAS.SOFA_INDEX_NAME);
return sofaIndex.iterator();
}
// For internal use only
public Sofa getSofaRef() {
if (this.mySofaRef == null) {
// create the SofaFS for _InitialView ...
// ... and reset mySofaRef to point to it
this.mySofaRef = this.createInitialSofa(null);
}
return this.mySofaRef;
}
// For internal use only
public InputStream getSofaDataStream(SofaFS aSofa) {
Sofa sofa = (Sofa) aSofa;
String sd = sofa.getLocalStringData();
if (null != sd) {
ByteArrayInputStream bis;
try {
bis = new ByteArrayInputStream(sd.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // never happen
}
return bis;
} else if (null != aSofa.getLocalFSData()) {
TOP fs = (TOP) sofa.getLocalFSData();
ByteBuffer buf = null;
switch(fs._getTypeCode()) {
case stringArrayTypeCode: {
StringBuilder sb = new StringBuilder();
final String[] theArray = ((StringArray) fs)._getTheArray();
for (int i = 0; i < theArray.length; i++) {
if (i != 0) {
sb.append('\n');
}
sb.append(theArray[i]);
}
try {
return new ByteArrayInputStream(sb.toString().getBytes("UTF-8") );
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // never happen
}
}
case intArrayTypeCode: {
final int[] theArray = ((IntegerArray) fs)._getTheArray();
(buf = ByteBuffer.allocate(theArray.length * 4)).asIntBuffer().put(theArray, 0, theArray.length);
break;
}
case floatArrayTypeCode: {
final float[] theArray = ((FloatArray) fs)._getTheArray();
(buf = ByteBuffer.allocate(theArray.length * 4)).asFloatBuffer().put(theArray, 0, theArray.length);
break;
}
case byteArrayTypeCode: {
final byte[] theArray = ((ByteArray) fs)._getTheArray();
buf = ByteBuffer.wrap(theArray);
break;
}
case shortArrayTypeCode: {
final short[] theArray = ((ShortArray) fs)._getTheArray();
(buf = ByteBuffer.allocate(theArray.length * 2)).asShortBuffer().put(theArray, 0, theArray.length);
break;
}
case longArrayTypeCode: {
final long[] theArray = ((LongArray) fs)._getTheArray();
(buf = ByteBuffer.allocate(theArray.length * 8)).asLongBuffer().put(theArray, 0, theArray.length);
break;
}
case doubleArrayTypeCode: {
final double[] theArray = ((DoubleArray) fs)._getTheArray();
(buf = ByteBuffer.allocate(theArray.length * 8)).asDoubleBuffer().put(theArray, 0, theArray.length);
break;
}
default:
Misc.internalError();
}
ByteArrayInputStream bis = new ByteArrayInputStream(buf.array());
return bis;
} else if (null != aSofa.getSofaURI()) {
URL url;
try {
url = new URL(aSofa.getSofaURI());
return url.openStream();
} catch (IOException exc) {
throw new CASRuntimeException(CASRuntimeException.SOFADATASTREAM_ERROR, exc.getMessage());
}
}
return null;
}
@Override
public<T extends FeatureStructure> FSIterator<T> createFilteredIterator(FSIterator<T> it, FSMatchConstraint cons) {
return new FilteredIterator<T>(it, cons);
}
public TypeSystemImpl commitTypeSystem() {
TypeSystemImpl ts = getTypeSystemImpl();
// For CAS pools, the type system could have already been committed
// Skip the initFSClassReg if so, because it may have been updated to a JCas
// version by another CAS processing in the pool
// @see org.apache.uima.cas.impl.FSClassRegistry
// avoid race: two instances of a CAS from a pool attempting to commit the
// ts
// at the same time
final ClassLoader cl = getJCasClassLoader();
synchronized (ts) {
if (!ts.isCommitted()) {
TypeSystemImpl tsc = ts.commit(getJCasClassLoader());
if (tsc != ts) {
installTypeSystemInAllViews(tsc);
ts = tsc;
}
}
}
svd.baseGenerators = svd.generators = ts.getGeneratorsForClassLoader(cl, false); // false - not PEAR
createIndexRepository();
return ts;
}
private void createIndexRepository() {
if (!this.getTypeSystemMgr().isCommitted()) {
throw new CASAdminException(CASAdminException.MUST_COMMIT_TYPE_SYSTEM);
}
if (this.indexRepository == null) {
this.indexRepository = new FSIndexRepositoryImpl(this);
}
}
@Override
public FSIndexRepositoryMgr getIndexRepositoryMgr() {
// assert(this.cas.getIndexRepository() != null);
return this.indexRepository;
}
/**
* @deprecated
* @param fs -
*/
@Deprecated
public void commitFS(FeatureStructure fs) {
getIndexRepository().addFS(fs);
}
@Override
public FeaturePath createFeaturePath() {
return new FeaturePathImpl();
}
// Implement the ConstraintFactory interface.
/**
* @see org.apache.uima.cas.admin.CASMgr#getTypeSystemMgr()
*/
@Override
public TypeSystemMgr getTypeSystemMgr() {
return getTypeSystemImpl();
}
@Override
public void reset() {
if (!this.svd.flushEnabled) {
throw new CASAdminException(CASAdminException.FLUSH_DISABLED);
}
if (this == this.svd.baseCAS) {
resetNoQuestions();
return;
}
// called from a CAS view.
// clear CAS ...
this.svd.baseCAS.resetNoQuestions();
}
public void resetNoQuestions() {
svd.casResets.incrementAndGet();
svd.clearCasReset();
if (trace) {
System.out.println("CAS Reset in thread " + Thread.currentThread().getName() +
" for CasId = " + getCasId() + ", new reset count = " + svd.casResets.get());
}
int numViews = this.getViewCount();
// Flush indexRepository for all views
for (int view = 1; view <= numViews; view++) {
CASImpl tcas = (CASImpl) ((view == 1) ? getInitialView() : getView(view));
if (tcas != null) {
tcas.indexRepository.flush();
// mySofaRef = -1 is a flag in initial view that sofa has not been set.
// For the initial view, it is possible to not have a sofa - it is set
// "lazily" upon the first need.
// all other views always have a sofa set. The sofaRef is set to 0,
// but will be set to the actual sofa addr in the cas when the view is
// initialized.
tcas.mySofaRef = null; // was in v2: (1 == view) ? -1 : 0;
}
}
this.svd.clearTrackingMarks();
// safety : in case this public method is called on other than the base cas
this.getBaseCAS().indexRepository.flush(); // for base view, other views flushed above
this.svd.clearSofaInfo(); // but keep initial view, and other views
// because setting up the index infrastructure is expensive
this.svd.viewCount = 1; // initial view
svd.traceFSid = 0;
if (traceFSs) svd.traceFScreationSb.setLength(0);
this.svd.componentInfo = null; // https://issues.apache.org/jira/browse/UIMA-5097
}
/**
* @deprecated Use {@link #reset reset()}instead.
*/
@Override
@Deprecated
public void flush() {
reset();
}
@Override
public FSIndexRepository getIndexRepository() {
if (this == this.svd.baseCAS) {
// BaseCas has no indexes for users
return null;
}
if (this.indexRepository.isCommitted()) {
return this.indexRepository;
}
return null;
}
FSIndexRepository getBaseIndexRepository() {
if (this.svd.baseCAS.indexRepository.isCommitted()) {
return this.svd.baseCAS.indexRepository;
}
return null;
}
FSIndexRepositoryImpl getBaseIndexRepositoryImpl() {
return this.svd.baseCAS.indexRepository;
}
void addSofaFsToIndex(SofaFS sofa) {
this.svd.baseCAS.getBaseIndexRepository().addFS(sofa);
}
void registerView(Sofa aSofa) {
this.mySofaRef = aSofa;
}
/**
* @see org.apache.uima.cas.CAS#fs2listIterator(FSIterator)
*/
@Override
public <T extends FeatureStructure> ListIterator<T> fs2listIterator(FSIterator<T> it) {
return new FSListIteratorImpl<T>(it);
}
/**
* @see org.apache.uima.cas.admin.CASMgr#getCAS()
*/
@Override
public CAS getCAS() {
if (this.indexRepository.isCommitted()) {
return this;
}
throw new CASAdminException(CASAdminException.MUST_COMMIT_INDEX_REPOSITORY);
}
// public void setFSClassRegistry(FSClassRegistry fsClassReg) {
// this.svd.casMetadata.fsClassRegistry = fsClassReg;
// }
// JCasGen'd cover classes use this to add their generators to the class
// registry
// Note that this now (June 2007) a no-op for JCasGen'd generators
// Also previously (but not now) used in JCas initialization to copy-down super generators to subtypes
// as needed
public FSClassRegistry getFSClassRegistry() {
return null;
// return getTypeSystemImpl().getFSClassRegistry();
}
/**
* @param fs the Feature Structure being updated
* @param fi the Feature of fs being updated, or null if fs is an array
* @param arrayIndexStart
* @param nbrOfConsecutive
*/
private void logFSUpdate(TOP fs, FeatureImpl fi, int arrayIndexStart, int nbrOfConsecutive) {
//log the FS
final Map<TOP, FsChange> changes = this.svd.modifiedPreexistingFSs;
//create or use last FsChange element
FsChange change = changes.computeIfAbsent(fs, key -> new FsChange(key));
if (fi == null) {
Misc.assertUie(arrayIndexStart >= 0);
change.addArrayData(arrayIndexStart, nbrOfConsecutive);
} else {
change.addFeatData(fi.getOffset());
}
}
/**
* @param fs the Feature Structure being updated
* @param arrayIndexStart
* @param nbrOfConsecutive
*/
private void logFSUpdate(TOP fs, PositiveIntSet indexesPlus1) {
//log the FS
final Map<TOP, FsChange> changes = this.svd.modifiedPreexistingFSs;
//create or use last FsChange element
FsChange change = changes.computeIfAbsent(fs, key -> new FsChange(key));
change.addArrayData(indexesPlus1);
}
private void logFSUpdate(TOP fs, FeatureImpl fi) {
logFSUpdate(fs, fi, -1, -1); // indicate non-array call
}
/**
* This is your link from the low-level API to the high-level API. Use this
* method to create a FeatureStructure object from an address. Note that the
* reverse is not supported by public APIs (i.e., there is currently no way to
* get at the address of a FeatureStructure. Maybe we will need to change
* that.
*
* The "create" in "createFS" is a misnomer - the FS must already be created.
*
* @param id The id of the feature structure to be created.
* @param <T> The Java class associated with this feature structure
* @return A FeatureStructure object.
*/
public <T extends TOP> T createFS(int id) {
return getFsFromId_checked(id);
}
public int getArraySize(CommonArrayFS fs) {
return fs.size();
}
@Override
public int ll_getArraySize(int id) {
return getArraySize(getFsFromId_checked(id));
}
final void setWithCheckAndJournal(TOP fs, FeatureImpl fi, Runnable setter) {
if (fs._inSetSortedIndex()) {
boolean wasRemoved = checkForInvalidFeatureSetting(fs, fi.getCode());
setter.run();
if (wasRemoved) {
maybeAddback(fs);
}
} else {
setter.run();
}
maybeLogUpdate(fs, fi);
}
final public void setWithCheckAndJournal(TOP fs, int featCode, Runnable setter) {
if (fs._inSetSortedIndex()) {
boolean wasRemoved = checkForInvalidFeatureSetting(fs, featCode);
setter.run();
if (wasRemoved) {
maybeAddback(fs);
}
} else {
setter.run();
}
maybeLogUpdate(fs, featCode);
}
// public void setWithCheck(FeatureStructureImplC fs, FeatureImpl feat, Runnable setter) {
// boolean wasRemoved = checkForInvalidFeatureSetting(fs, feat);
// setter.run();
// if (wasRemoved) {
// maybeAddback(fs);
// }
// }
/**
* This method called by setters in JCas gen'd classes when
* the setter must check for journaling
* @param fs -
* @param fi -
* @param setter -
*/
public void setWithJournal(FeatureStructureImplC fs, FeatureImpl fi, Runnable setter) {
setter.run();
maybeLogUpdate(fs, fi);
}
public boolean isLoggingNeeded(FeatureStructureImplC fs) {
return this.svd.trackingMark != null && !this.svd.trackingMark.isNew(fs._id);
}
/**
* @param fs the Feature Structure being updated
* @param feat the feature of fs being updated, or null if fs is a primitive array
* @param i the index being updated
*/
final public void maybeLogArrayUpdate(FeatureStructureImplC fs, FeatureImpl feat, int i) {
if (isLoggingNeeded(fs)) {
this.logFSUpdate((TOP) fs, feat, i, 1);
}
}
/**
* @param fs the Feature Structure being updated
* @param indexesPlus1 - a set of indexes (plus 1) that have been update
*/
final public void maybeLogArrayUpdates(FeatureStructureImplC fs, PositiveIntSet indexesPlus1) {
if (isLoggingNeeded(fs)) {
this.logFSUpdate((TOP) fs, indexesPlus1);
}
}
/**
* @param fs a primitive array FS
* @param startingIndex -
* @param length number of consequtive items
*/
public void maybeLogArrayUpdates(FeatureStructureImplC fs, int startingIndex, int length) {
if (isLoggingNeeded(fs)) {
this.logFSUpdate((TOP) fs, null, startingIndex, length);
}
}
final public void maybeLogUpdate(FeatureStructureImplC fs, FeatureImpl feat) {
if (isLoggingNeeded(fs)) {
this.logFSUpdate((TOP) fs, feat);
}
}
final public void maybeLogUpdate(FeatureStructureImplC fs, int featCode) {
if (isLoggingNeeded(fs)) {
this.logFSUpdate((TOP)fs, getFeatFromCode_checked(featCode));
}
}
final public boolean isLogging() {
return this.svd.trackingMark != null;
}
// /**
// * Common setter code for features in Feature Structures
// *
// * These come in two styles: one with int values, one with Object values
// * Object values are FS or Strings or JavaObjects
// */
//
// /**
// * low level setter
// *
// * @param fs the feature structure
// * @param feat the feature to set
// * @param value -
// */
//
// public void setFeatureValue(FeatureStructureImplC fs, FeatureImpl feat, int value) {
// fs.setIntValue(feat, value);
//// boolean wasRemoved = checkForInvalidFeatureSetting(fs, feat.getCode());
//// fs._intData[feat.getAdjustedOffset()] = value;
//// if (wasRemoved) {
//// maybeAddback(fs);
//// }
//// maybeLogUpdate(fs, feat);
// }
/**
* version for longs, uses two slots
* Only called from FeatureStructureImplC after determining
* there is no local field to use
* Is here because of 3 calls to things in this class
* @param fsIn the feature structure
* @param feat the feature to set
* @param v -
*/
public void setLongValue(FeatureStructureImplC fsIn, FeatureImpl feat, long v) {
TOP fs = (TOP) fsIn;
if (fs._inSetSortedIndex()) {
boolean wasRemoved = checkForInvalidFeatureSetting(fs, feat.getCode());
fs._setLongValueNcNj(feat, v);
if (wasRemoved) {
maybeAddback(fs);
}
} else {
fs._setLongValueNcNj(feat, v);
}
maybeLogUpdate(fs, feat);
}
void setFeatureValue(int fsRef, int featureCode, TOP value) {
getFsFromId_checked(fsRef).setFeatureValue(getFeatFromCode_checked(featureCode), value);
}
/**
* Supports setting slots to "0" for null values
* @param fs The feature structure to update
* @param feat the feature to update-
* @param s the string representation of the value, could be null
*/
public void setFeatureValueFromString(FeatureStructureImplC fs, FeatureImpl feat, String s) {
final TypeImpl range = feat.getRangeImpl();
if (fs instanceof Sofa) {
// sofa has special setters
Sofa sofa = (Sofa) fs;
switch (feat.getCode()) {
case sofaMimeFeatCode : sofa.setMimeType(s); break;
case sofaStringFeatCode: sofa.setLocalSofaData(s); break;
case sofaUriFeatCode: sofa.setRemoteSofaURI(s); break;
default: // left empty - ignore trying to set final fields
}
return;
}
if (feat.isInInt) {
switch (range.getCode()) {
case floatTypeCode : fs.setFloatValue(feat, (s == null) ? 0F : Float.parseFloat(s)); break;
case booleanTypeCode : fs.setBooleanValue(feat, (s == null) ? false : Boolean.parseBoolean(s)); break;
case longTypeCode : fs.setLongValue(feat, (s == null) ? 0L : Long.parseLong(s)); break;
case doubleTypeCode : fs.setDoubleValue(feat, (s == null) ? 0D : Double.parseDouble(s)); break;
case byteTypeCode : fs.setByteValue(feat, (s == null) ? 0 : Byte.parseByte(s)); break;
case shortTypeCode : fs.setShortValue(feat, (s == null) ? 0 : Short.parseShort(s)); break;
case intTypeCode : fs.setIntValue(feat, (s == null) ? 0 : Integer.parseInt(s)); break;
default: fs.setIntValue(feat, (s == null) ? 0 : Integer.parseInt(s));
}
} else if (range.isRefType) {
if (s == null) {
fs.setFeatureValue(feat, null);
} else {
// Setting a reference value "{0}" from a string is not supported.
throw new CASRuntimeException(CASRuntimeException.SET_REF_FROM_STRING_NOT_SUPPORTED, feat.getName());
}
} else if (range.isStringOrStringSubtype()) { // includes TypeImplSubString
// is String or Substring
fs.setStringValue(feat, (s == null) ? null : s);
// } else if (range == getTypeSystemImpl().javaObjectType) {
// fs.setJavaObjectValue(feat, (s == null) ? null : deserializeJavaObject(s));
} else {
Misc.internalError();
}
}
// private Object deserializeJavaObject(String s) {
// throw new UnsupportedOperationException("Deserializing JavaObjects not yet implemented");
// }
//
// static String serializeJavaObject(Object s) {
// throw new UnsupportedOperationException("Serializing JavaObjects not yet implemented");
// }
/*
* This should be the only place where the encoding of floats and doubles in terms of ints is specified
* Someday we may want to preserve NAN things using "raw" versions
*/
public static final float int2float(int i) {
return Float.intBitsToFloat(i);
}
public static final int float2int(float f) {
return Float.floatToIntBits(f);
}
public static final double long2double(long l) {
return Double.longBitsToDouble(l);
}
public static final long double2long(double d) {
return Double.doubleToLongBits(d);
}
// Type access methods.
public boolean isStringType(Type type) {
return type instanceof TypeImpl_string;
}
public boolean isArrayOfFsType(Type type) {
return ((TypeImpl) type).isArray();
}
public boolean isPrimitiveArrayType(Type type) {
return (type instanceof TypeImpl_array) && ! type.getComponentType().isPrimitive();
}
public boolean isIntArrayType(Type type) {
return (type == getTypeSystemImpl().intArrayType);
}
public boolean isFloatArrayType(Type type) {
return ((TypeImpl)type).getCode() == floatArrayTypeCode;
}
public boolean isStringArrayType(Type type) {
return ((TypeImpl)type).getCode() == stringArrayTypeCode;
}
public boolean isBooleanArrayType(Type type) {
return ((TypeImpl)type).getCode() == booleanArrayTypeCode;
}
public boolean isByteArrayType(Type type) {
return ((TypeImpl)type).getCode() == byteArrayTypeCode;
}
public boolean isShortArrayType(Type type) {
return ((TypeImpl)type).getCode() == byteArrayTypeCode;
}
public boolean isLongArrayType(Type type) {
return ((TypeImpl)type).getCode() == longArrayTypeCode;
}
public boolean isDoubleArrayType(Type type) {
return ((TypeImpl)type).getCode() == doubleArrayTypeCode;
}
public boolean isFSArrayType(Type type) {
return ((TypeImpl)type).getCode() == fsArrayTypeCode;
}
public boolean isIntType(Type type) {
return ((TypeImpl)type).getCode() == intTypeCode;
}
public boolean isFloatType(Type type) {
return ((TypeImpl)type).getCode() == floatTypeCode;
}
public boolean isByteType(Type type) {
return ((TypeImpl)type).getCode() == byteTypeCode;
}
public boolean isBooleanType(Type type) {
return ((TypeImpl)type).getCode() == floatTypeCode;
}
public boolean isShortType(Type type) {
return ((TypeImpl)type).getCode() == shortTypeCode;
}
public boolean isLongType(Type type) {
return ((TypeImpl)type).getCode() == longTypeCode;
}
public boolean isDoubleType(Type type) {
return ((TypeImpl)type).getCode() == doubleTypeCode;
}
/*
* Only called on base CAS
*/
/**
* @see org.apache.uima.cas.admin.CASMgr#initCASIndexes()
*/
@Override
public void initCASIndexes() throws CASException {
final TypeSystemImpl ts = getTypeSystemImpl();
if (!ts.isCommitted()) {
throw new CASException(CASException.MUST_COMMIT_TYPE_SYSTEM);
}
FSIndexComparator comp = this.indexRepository.createComparator();
comp.setType(ts.sofaType);
comp.addKey(ts.sofaNum, FSIndexComparator.STANDARD_COMPARE);
this.indexRepository.createIndex(comp, CAS.SOFA_INDEX_NAME, FSIndex.BAG_INDEX);
comp = this.indexRepository.createComparator();
comp.setType(ts.annotType);
comp.addKey(ts.startFeat, FSIndexComparator.STANDARD_COMPARE);
comp.addKey(ts.endFeat, FSIndexComparator.REVERSE_STANDARD_COMPARE);
comp.addKey(this.indexRepository.getDefaultTypeOrder(), FSIndexComparator.STANDARD_COMPARE);
this.indexRepository.createIndex(comp, CAS.STD_ANNOTATION_INDEX);
}
// ///////////////////////////////////////////////////////////////////////////
// CAS support ... create CAS view of aSofa
// For internal use only
public CAS getView(int sofaNum) {
return getViewFromSofaNbr(sofaNum);
}
@Override
public CAS getCurrentView() {
return getView(CAS.NAME_DEFAULT_SOFA);
}
// ///////////////////////////////////////////////////////////////////////////
// JCas support
@Override
public JCas getJCas() {
if (this.jcas == null) {
this.jcas = JCasImpl.getJCas(this);
}
return this.jcas;
}
public JCasImpl getJCasImpl() {
if (this.jcas == null) {
this.jcas = JCasImpl.getJCas(this);
}
return this.jcas;
}
// /**
// * Internal use only
// *
// * @return corresponding JCas, assuming it exists
// */
// public JCas getExistingJCas() {
// return this.jcas;
// }
//
// Create JCas view of aSofa
@Override
public JCas getJCas(SofaFS aSofa) throws CASException {
// Create base JCas, if needed
this.svd.baseCAS.getJCas();
return getView(aSofa).getJCas();
/*
* // If a JCas already exists for this Sofa, return it JCas aJCas = (JCas)
* this.svd.baseCAS.sofa2jcasMap.get(Integer.valueOf(aSofa.getSofaRef())); if
* (null != aJCas) { return aJCas; } // Get view of aSofa CASImpl view =
* (CASImpl) getView(aSofa); // wrap in JCas aJCas = view.getJCas();
* this.sofa2jcasMap.put(Integer.valueOf(aSofa.getSofaRef()), aJCas); return
* aJCas;
*/
}
/**
* @deprecated
*/
@Override
@Deprecated
public JCas getJCas(SofaID aSofaID) throws CASException {
SofaFS sofa = getSofa(aSofaID);
// sofa guaranteed to be non-null by above method.
return getJCas(sofa);
}
private CASImpl getViewFromSofaNbr(int nbr) {
final ArrayList<CASImpl> sn2v = this.svd.sofaNbr2ViewMap;
if (nbr < sn2v.size()) {
return sn2v.get(nbr);
}
return null;
}
void setViewForSofaNbr(int nbr, CASImpl view) {
Misc.setWithExpand(this.svd.sofaNbr2ViewMap, nbr, view);
}
// For internal platform use only
CASImpl getInitialView() {
CASImpl couldBeThis = getViewFromSofaNbr(1);
if (couldBeThis != null) {
return couldBeThis;
}
// create the initial view, without a Sofa
CASImpl aView = new CASImpl(this.svd.baseCAS, (SofaFS) null);
setViewForSofaNbr(1, aView);
assert (this.svd.viewCount <= 1);
this.svd.viewCount = 1;
return aView;
}
@Override
public CAS createView(String aSofaID) {
// do sofa mapping for current component
String absoluteSofaName = null;
if (getCurrentComponentInfo() != null) {
absoluteSofaName = getCurrentComponentInfo().mapToSofaID(aSofaID);
}
if (absoluteSofaName == null) {
absoluteSofaName = aSofaID;
}
// Can't use name of Initial View
if (CAS.NAME_DEFAULT_SOFA.equals(absoluteSofaName)) {
throw new CASRuntimeException(CASRuntimeException.SOFANAME_ALREADY_EXISTS, aSofaID);
}
Sofa newSofa = createSofa(absoluteSofaName, null);
CAS newView = getView(newSofa);
((CASImpl) newView).registerView(newSofa);
return newView;
}
@Override
public CAS getView(String aSofaID) {
// do sofa mapping for current component
String absoluteSofaName = null;
if (getCurrentComponentInfo() != null) {
absoluteSofaName = getCurrentComponentInfo().mapToSofaID(aSofaID);
}
if (absoluteSofaName == null) {
absoluteSofaName = aSofaID;
}
// if this resolves to the Initial View, return view(1)...
// ... as the Sofa for this view may not exist yet
if (CAS.NAME_DEFAULT_SOFA.equals(absoluteSofaName)) {
return getInitialView();
}
// get Sofa and switch to view
SofaFS sofa = getSofa(absoluteSofaName);
// sofa guaranteed to be non-null by above method
// unless sofa doesn't exist, which will cause a throw.
return getView(sofa);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.CAS#getView(org.apache.uima.cas.SofaFS)
*
* Callers of this can have created Sofas in the CAS without views: using the
* old deprecated createSofa apis (this is being fixed so these will create
* the views) via deserialization, which will put the sofaFSs into the CAS
* without creating the views, and then call this to create the views. - for
* deserialization: there are 2 kinds: 1 is xmi the other is binary. - for
* xmi: there is 1.4.x compatible and 2.1 compatible. The older format can
* have sofaNbrs in the order 2, 3, 4, 1 (initial sofa), 5, 6, 7 The newer
* format has them in order. For deserialized sofas, we insure here that there
* are no duplicates. This is not done in the deserializers - they use either
* heap dumping (binary) or generic fs creators (xmi).
*
* Goal is to detect case where check is needed (sofa exists, but view not yet
* created). This is done by looking for cases where sofaNbr &gt; curViewCount.
* This only works if the sofaNbrs go up by 1 (except for the initial sofa) in
* the input sequence of calls.
*/
@Override
public CASImpl getView(SofaFS aSofa) {
Sofa sofa = (Sofa) aSofa;
final int sofaNbr = sofa.getSofaRef();
// final Integer sofaNbrInteger = Integer.valueOf(sofaNbr);
CASImpl aView = getViewFromSofaNbr(sofaNbr);
if (null == aView) {
// This is the deserializer case, or the case where an older API created a
// sofa,
// which is now creating the associated view
// create a new CAS view
aView = new CASImpl(this.svd.baseCAS, sofa);
setViewForSofaNbr(sofaNbr, aView);
verifySofaNameUniqueIfDeserializedViewAdded(sofaNbr, sofa);
return aView;
}
// for deserialization - might be reusing a view, and need to tie new Sofa
// to old View
if (null == aView.mySofaRef) {
aView.mySofaRef = sofa;
}
verifySofaNameUniqueIfDeserializedViewAdded(sofaNbr, aSofa);
return aView;
}
// boolean isSofaView(int sofaAddr) {
// if (mySofaRef == null) {
// // don't create initial sofa
// return false;
// }
// return mySofaRef == sofaAddr;
// }
/*
* for Sofas being added (determined by sofaNbr &gt; curViewCount): verify sofa
* name is not already present, and record it for future tests
*
* Only should do the name test & update in the case of deserialized new sofas
* coming in. These will come in, in order. Exception is "_InitialView" which
* could come in the middle. If it comes in the middle, no test will be done
* for duplicates, and it won't be added to set of known names. This is ok
* because the createVIew special cases this test. Users could corrupt an xmi
* input, which would make this logic fail.
*/
private void verifySofaNameUniqueIfDeserializedViewAdded(int sofaNbr, SofaFS aSofa) {
final int curViewCount = this.svd.viewCount;
if (curViewCount < sofaNbr) {
// Only true for deserialized sofas with new views being either created,
// or
// hooked-up from CASes that were freshly reset, which have multiple
// views.
// Assume sofa numbers are incrementing by 1
assert (sofaNbr == curViewCount + 1);
this.svd.viewCount = sofaNbr;
String id = aSofa.getSofaID();
// final Feature idFeat =
// getTypeSystem().getFeatureByFullName(CAS.FEATURE_FULL_NAME_SOFAID);
// String id =
// ll_getStringValue(((FeatureStructureImpl)aSofa).getAddress(),
// ((FeatureImpl) idFeat).getCode());
Misc.assertUie(this.svd.sofaNameSet.contains(id));
// this.svd.sofaNameSet.add(id);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getTypeSystem()
*/
@Override
public LowLevelTypeSystem ll_getTypeSystem() {
return getTypeSystemImpl().getLowLevelTypeSystem();
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getIndexRepository()
*/
@Override
public LowLevelIndexRepository ll_getIndexRepository() {
return this.indexRepository;
}
/**
*
* @param fs
* @param domType
* @param featCode
*/
private final void checkLowLevelParams(TOP fs, TypeImpl domType, int featCode) {
checkFeature(featCode);
checkTypeHasFeature(domType, featCode);
}
/**
* Check that the featCode is a feature of the domain type
* @param domTypeCode
* @param featCode
*/
private final void checkTypeHasFeature(TypeImpl domainType, int featureCode) {
checkTypeHasFeature(domainType, getFeatFromCode_checked(featureCode));
}
private final void checkTypeHasFeature(TypeImpl domainType, FeatureImpl feature) {
if (!domainType.isAppropriateFeature(feature)) {
throw new LowLevelException(LowLevelException.FEAT_DOM_ERROR,
Integer.valueOf(domainType.getCode()),
domainType.getName(),
Integer.valueOf(feature.getCode()),
feature.getName());
}
}
/**
* Check the range is appropriate for this type/feature. Throws
* LowLevelException if it isn't.
*
* @param domType
* domain type
* @param ranType
* range type
* @param feat
* feature
*/
public final void checkTypingConditions(Type domType, Type ranType, Feature feat) {
TypeImpl domainTi = (TypeImpl) domType;
FeatureImpl fi = (FeatureImpl) feat;
checkTypeHasFeature(domainTi, fi);
if (!((TypeImpl) fi.getRange()).subsumes((TypeImpl) ranType)) {
throw new LowLevelException(LowLevelException.FEAT_RAN_ERROR,
Integer.valueOf(fi.getCode()),
feat.getName(),
Integer.valueOf(((TypeImpl)ranType).getCode()),
ranType.getName());
}
}
/**
* Validate a feature's range is a ref to a feature structure
* @param featCode
* @throws LowLevelException
*/
private final void checkFsRan(FeatureImpl fi) throws LowLevelException {
if (!fi.getRangeImpl().isRefType) {
throw new LowLevelException(LowLevelException.FS_RAN_TYPE_ERROR,
Integer.valueOf(fi.getCode()),
fi.getName(),
fi.getRange().getName());
}
}
private final void checkFeature(int featureCode) {
if (!getTypeSystemImpl().isFeature(featureCode)) {
throw new LowLevelException(LowLevelException.INVALID_FEATURE_CODE, Integer.valueOf(featureCode));
}
}
private TypeImpl getTypeFromCode(int typeCode) {
return getTypeSystemImpl().getTypeForCode(typeCode);
}
private TypeImpl getTypeFromCode_checked(int typeCode) {
return getTypeSystemImpl().getTypeForCode_checked(typeCode);
}
private FeatureImpl getFeatFromCode_checked(int featureCode) {
return getTypeSystemImpl().getFeatureForCode_checked(featureCode);
}
public final <T extends TOP> T getFsFromId_checked(int fsRef) {
T r = getFsFromId(fsRef);
if (r == null) {
if (fsRef == 0) {
return null;
}
LowLevelException e = new LowLevelException(LowLevelException.INVALID_FS_REF, Integer.valueOf(fsRef));
// this form to enable seeing this even if the
// throwable is silently handled.
// System.err.println("debug " + e);
throw e;
}
return r;
}
@Override
public final boolean ll_isRefType(int typeCode) {
return getTypeFromCode(typeCode).isRefType;
}
@Override
public final int ll_getTypeClass(int typeCode) {
return TypeSystemImpl.getTypeClass(getTypeFromCode(typeCode));
}
// backwards compatibility only
@Override
public final int ll_createFS(int typeCode) {
return ll_createFS(typeCode, true);
}
@Override
public final int ll_createFS(int typeCode, boolean doCheck) {
TypeImpl ti = (TypeImpl) getTypeSystemImpl().ll_getTypeForCode(typeCode);
if (doCheck) {
if (ti == null || !ti.isCreatableAndNotBuiltinArray()) {
throw new LowLevelException(LowLevelException.CREATE_FS_OF_TYPE_ERROR, Integer.valueOf(typeCode));
}
}
TOP fs = (TOP) createFS(ti);
if (!fs._isPearTrampoline()) {
if (IS_ALWAYS_HOLD_ONTO_FSS) {
svd.id2fs.putUnconditionally(fs); // hold on to it if nothing else is
} else {
svd.id2fs.put(fs);
}
}
return fs._id;
}
/**
* used for ll_setIntValue which changes type code
* @param ti - the type of the created FS
* @param id - the id to use
* @return the FS
*/
private TOP createFsWithExistingId(TypeImpl ti, int id) {
svd.reuseId = id;
try {
TOP fs = createFS(ti);
svd.id2fs.putChange(id, fs);
return fs;
} finally {
svd.reuseId = 0;
}
}
/*
/**
* @param arrayLength
* @return the id of the created array
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_createArray(int, int)
*/
@Override
public int ll_createArray(int typeCode, int arrayLength) {
TOP fs = createArray(getTypeFromCode_checked(typeCode), arrayLength);
if (IS_ALWAYS_HOLD_ONTO_FSS) {
svd.id2fs.putUnconditionally(fs);
} else {
svd.id2fs.put(fs);
}
return fs._id;
}
/**
* (for backwards compatibility with V2 CASImpl)
* Create a temporary (i.e., per document) array FS on the heap.
*
* @param type
* The type code of the array to be created.
* @param len
* The length of the array to be created.
* @return -
* @exception ArrayIndexOutOfBoundsException
* If <code>type</code> is not a type.
*/
public int createTempArray(int type, int len) {
return ll_createArray(type, len);
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createByteArray(int arrayLength) {
TOP fs = createArray(getTypeSystemImpl().byteArrayType, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createBooleanArray(int arrayLength) {
TOP fs = createArray(getTypeSystemImpl().booleanArrayType, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createShortArray(int arrayLength) {
TOP fs = createArray(getTypeSystemImpl().shortArrayType, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createLongArray(int arrayLength) {
TOP fs = createArray(getTypeSystemImpl().longArrayType, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createDoubleArray(int arrayLength) {
TOP fs = createArray(getTypeSystemImpl().doubleArrayType, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
/**
* @param arrayLength -
* @return the id of the created array
*/
@Override
public int ll_createArray(int typeCode, int arrayLength, boolean doChecks) {
TypeImpl ti = getTypeFromCode_checked(typeCode);
if (doChecks) {
if (!ti.isArray()) {
throw new LowLevelException(LowLevelException.CREATE_ARRAY_OF_TYPE_ERROR, Integer.valueOf(typeCode), ti.getName());
}
if (arrayLength < 0) {
throw new LowLevelException(LowLevelException.ILLEGAL_ARRAY_LENGTH, Integer.valueOf(arrayLength));
}
}
TOP fs = createArray(ti, arrayLength);
svd.id2fs.put(fs);
return fs._id;
}
public void validateArraySize(int length) {
if (length < 0) {
/** Array size must be &gt;= 0. */
throw new CASRuntimeException(CASRuntimeException.ILLEGAL_ARRAY_SIZE);
}
}
/**
* Safety - any time the low level API to a FS is requested,
* hold on to that FS until CAS reset to mimic how v2 works.
*/
@Override
public final int ll_getFSRef(FeatureStructure fs) {
if (null == fs) {
return NULL;
}
TOP fst = (TOP) fs;
if (fst._isPearTrampoline()) {
return fst._id; // no need to hold on to this one - it's in jcas hash maps
}
// uncond. because this method can be called multiple times
svd.id2fs.putUnconditionally(fst); // hold on to it
return ((FeatureStructureImplC)fs)._id;
}
@Override
public <T extends TOP> T ll_getFSForRef(int id) {
return getFsFromId_checked(id);
}
/**
* Handle some unusual backwards compatibility cases
* featureCode = 0 - implies getting the type code
* feature range is int - normal
* feature range is a fs reference, return the id
* feature range is a string: add the string if not already present to the string heap, return the int handle.
* @param fsRef -
* @param featureCode -
* @return -
*/
@Override
public final int ll_getIntValue(int fsRef, int featureCode) {
TOP fs = getFsFromId_checked(fsRef);
if (featureCode == 0) {
return fs._getTypeImpl().getCode(); // case where the type is being requested
}
FeatureImpl fi = getFeatFromCode_checked(featureCode);
SlotKind kind = fi.getSlotKind();
switch(kind) {
case Slot_HeapRef:
return fs.getFeatureValue(fi)._id;
case Slot_Boolean:
case Slot_Byte:
case Slot_Short:
case Slot_Int:
case Slot_Float:
return fs._getIntValueNc(fi);
case Slot_StrRef:
return getCodeForString(fs._getStringValueNc(fi));
case Slot_LongRef:
return getCodeForLong(fs._getLongValueNc(fi));
case Slot_DoubleRef:
return getCodeForLong(CASImpl.double2long(fs._getDoubleValueNc(fi)));
default: throw new CASRuntimeException(
CASRuntimeException.INAPPROP_RANGE,
fi.getName(),
"int",
fi.getRange().getName());
}
}
// public final int ll_getIntValueFeatOffset(int fsRef, int featureOffset) {
// return ll_getFSForRef(fsRef)._intData[featureOffset];
// }
@Override
public final float ll_getFloatValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getFloatValue(getFeatFromCode_checked(featureCode));
}
@Override
public final String ll_getStringValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getStringValue(getFeatFromCode_checked(featureCode));
}
// public final String ll_getStringValueFeatOffset(int fsRef, int featureOffset) {
// return (String) getFsFromId_checked(fsRef)._refData[featureOffset];
// }
@Override
public final int ll_getRefValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getFeatureValue(getFeatFromCode_checked(featureCode))._id();
}
// public final int ll_getRefValueFeatOffset(int fsRef, int featureOffset) {
// return ((FeatureStructureImplC)getFsFromId_checked(fsRef)._refData[featureOffset]).id();
// }
@Override
public final int ll_getIntValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getIntValue(fsRef, featureCode);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getFloatValue(int, int,
* boolean)
*/
@Override
public final float ll_getFloatValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getFloatValue(fsRef, featureCode);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getStringValue(int, int,
* boolean)
*/
@Override
public final String ll_getStringValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getStringValue(fsRef, featureCode);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getRefValue(int, int, boolean)
*/
@Override
public final int ll_getRefValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkFsRefConditions(fsRef, featureCode);
}
return getFsFromId_checked(fsRef).getFeatureValue(getFeatFromCode_checked(featureCode))._id();
}
/**
* This is the method all normal FS feature "setters" call before doing the set operation
* on values where the range could be used as an index key.
* <p>
* If enabled, it will check if the update may corrupt any index in any view. The check tests
* whether the feature is being used as a key in one or more indexes and if the FS is in one or more
* corruptable view indexes.
* <p>
* If true, then:
* <ul>
* <li>it may remove and remember (for later adding-back) the FS from all corruptable indexes
* (bag indexes are not corruptable via updating, so these are skipped).
* The addback occurs later either via an explicit call to do so, or the end of a protectIndex block, or.
* (if autoIndexProtect is enabled) after the individual feature update is completed.</li>
* <li>it may give a WARN level message to the log. This enables users to
* implement their own optimized handling of this for "high performance"
* applications which do not want the overhead of runtime checking. </li></ul>
* <p>
*
* @param fs - the FS to test if it is in the indexes
* @param featCode - the feature being tested
* @return true if something may need to be added back
*/
private boolean checkForInvalidFeatureSetting(TOP fs, int featCode) {
if (doInvalidFeatSettingCheck(fs)) {
if (!svd.featureCodesInIndexKeys.get(featCode)) { // skip if no index uses this feature
return false;
}
boolean wasRemoved = checkForInvalidFeatureSetting2(fs);
if (wasRemoved && doCorruptReport()) {
featModWhileInIndexReport(fs, featCode);
}
return wasRemoved;
}
return false;
}
/**
* version for deserializers, and for set document language, using their own store for toBeAdded
* Doesn't report updating of corruptable slots.
* @param fs -
* @param featCode -
* @param toBeAdded -
* @return -
*/
boolean checkForInvalidFeatureSetting(TOP fs, int featCode, FSsTobeAddedback toBeAdded) {
if (doInvalidFeatSettingCheck(fs)) {
if (!svd.featureCodesInIndexKeys.get(featCode)) { // skip if no index uses this feature
return false;
}
boolean wasRemoved = removeFromCorruptableIndexAnyView(fs, toBeAdded);
// if (wasRemoved && doCorruptReport()) {
// featModWhileInIndexReport(fs, featCode);
// }
return wasRemoved;
}
return false;
}
/**
* version for deserializers, using their own store for toBeAdded
* and not bothering to check for particular features
* Doesn't report updating of corruptable slots.
* @param fs -
* @param featCode -
* @param toBeAdded -
* @return -
*/
boolean checkForInvalidFeatureSetting(TOP fs, FSsTobeAddedback toBeAdded) {
if (doInvalidFeatSettingCheck(fs)) {
boolean wasRemoved = removeFromCorruptableIndexAnyView(fs, toBeAdded);
// if (wasRemoved && doCorruptReport()) {
// featModWhileInIndexReport(fs, null);
// }
return wasRemoved;
}
return false;
}
// // version of above, but using jcasFieldRegistryIndex
// private boolean checkForInvalidFeatureSettingJFRI(TOP fs, int jcasFieldRegistryIndex) {
// if (doInvalidFeatSettingCheck(fs) &&
// svd.featureJiInIndexKeys.get(jcasFieldRegistryIndex)) {
//
// boolean wasRemoved = checkForInvalidFeatureSetting2(fs);
//
//// if (wasRemoved && doCorruptReport()) {
//// featModWhileInIndexReport(fs, getFeatFromRegistry(jcasFieldRegistryIndex));
//// }
// return wasRemoved;
// }
// return false;
// }
private boolean checkForInvalidFeatureSetting2(TOP fs) {
final int ssz = svd.fssTobeAddedback.size();
// next method skips if the fsRef is not in the index (cache)
boolean wasRemoved = removeFromCorruptableIndexAnyView(
fs,
(ssz > 0) ? getAddback(ssz) : // validates single not in use
getAddbackSingle() // validates single usage at a time
);
if (!wasRemoved && svd.fsTobeAddedbackSingleInUse) {
svd.fsTobeAddedbackSingleInUse = false;
}
return wasRemoved;
}
// public FeatureImpl getFeatFromRegistry(int jcasFieldRegistryIndex) {
// return getFSClassRegistry().featuresFromJFRI[jcasFieldRegistryIndex];
// }
private boolean doCorruptReport() {
return
// skip message if wasn't removed
// skip message if protected in explicit block
IS_REPORT_FS_UPDATE_CORRUPTS_INDEX && svd.fssTobeAddedback.size() == 0;
}
/**
*
* @param fs -
* @return false if the fs is not in a set or sorted index (bit in fs), or
* the auto protect is disabled and we're not in an explicit protect block
*/
private boolean doInvalidFeatSettingCheck(TOP fs) {
if (!fs._inSetSortedIndex()) {
return false;
}
final int ssz = svd.fssTobeAddedback.size();
// skip if protection is disabled, and no explicit protection block
if (IS_DISABLED_PROTECT_INDEXES && ssz == 0) {
return false;
}
return true;
}
private void featModWhileInIndexReport(FeatureStructure fs, int featCode) {
featModWhileInIndexReport(fs, getFeatFromCode_checked(featCode));
}
private void featModWhileInIndexReport(FeatureStructure fs, FeatureImpl fi) {
// prepare a message which includes the feature which is a key, the fs, and
// the call stack.
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
new Throwable().printStackTrace(pw);
pw.close();
String msg = String.format(
"While FS was in the index, the feature \"%s\""
+ ", which is used as a key in one or more indexes, "
+ "was modified\n FS = \"%s\"\n%s%n",
(fi == null)? "for-all-features" : fi.getName(),
fs.toString(),
sw.toString());
UIMAFramework.getLogger().log(Level.WARNING, msg);
if (IS_THROW_EXCEPTION_CORRUPT_INDEX) {
throw new UIMARuntimeException(UIMARuntimeException.ILLEGAL_FS_FEAT_UPDATE, new Object[]{});
}
}
/**
* Only called if there was something removed that needs to be added back
*
* skip the addback (to defer it until later) if:
* - running in block mode (you can tell this if svd.fssTobeAddedback.size() &gt; 0) or
* if running in block mode, the add back is delayed until the end of the block
*
* @param fs the fs to add back
*/
public void maybeAddback(TOP fs) {
if (svd.fssTobeAddedback.size() == 0) {
assert(svd.fsTobeAddedbackSingleInUse);
svd.fsTobeAddedbackSingle.addback(fs);
svd.fsTobeAddedbackSingleInUse = false;
}
}
boolean removeFromCorruptableIndexAnyView(final TOP fs, FSsTobeAddedback toBeAdded) {
return removeFromIndexAnyView(fs, toBeAdded, FSIndexRepositoryImpl.SKIP_BAG_INDEXES);
}
/**
* This might be called from low level set int value, if we support switching types, and we want to
* remove the old type from all indexes.
* @param fs the fs to maybe remove
* @param toBeAdded a place to record the removal so we can add it back later
* @param isSkipBagIndexes is true usually, we don't need to remove/readd to bag indexes (except for the case
* of supporting switching types via low level set int for v2 backwards compatibility)
* @return true if was removed from one or more indexes
*/
boolean removeFromIndexAnyView(final TOP fs, FSsTobeAddedback toBeAdded, boolean isSkipBagIndexes) {
final TypeImpl ti = ((FeatureStructureImplC)fs)._getTypeImpl();
if (ti.isAnnotationBaseType()) {
boolean r = removeAndRecord(fs,
(FSIndexRepositoryImpl) fs._casView.getIndexRepository(),
toBeAdded,
isSkipBagIndexes);
fs._resetInSetSortedIndex();
return r;
}
// not a subtype of AnnotationBase, need to check all views (except base)
// sofas indexed in the base view are not corruptable.
final Iterator<CAS> viewIterator = getViewIterator();
boolean wasRemoved = false;
while (viewIterator.hasNext()) {
wasRemoved |= removeAndRecord(fs,
(FSIndexRepositoryImpl) viewIterator.next().getIndexRepository(),
toBeAdded,
isSkipBagIndexes);
}
fs._resetInSetSortedIndex();
return wasRemoved;
}
/**
* remove a FS from all indexes in this view (except bag indexes, if isSkipBagIndex is true)
* @param fs the fs to be removed
* @param ir the view
* @param toBeAdded the place to record how many times it was in the index, per view
* @param isSkipBagIndex set to true for corruptable removes, false for remove in all cases from all indexes
* @return true if it was removed, false if it wasn't in any corruptable index.
*/
private boolean removeAndRecord(TOP fs, FSIndexRepositoryImpl ir, FSsTobeAddedback toBeAdded, boolean isSkipBagIndex) {
boolean wasRemoved = ir.removeFS_ret(fs, isSkipBagIndex);
if (wasRemoved) {
toBeAdded.recordRemove(fs, ir, 1);
}
return wasRemoved;
}
/**
* Special considerations:
* Interface with corruption checking
* For backwards compatibility:
* handle cases where feature is:
* int - normal
* 0 - change type code
* a ref: treat int as FS "addr"
* not an int: handle like v2 where reasonable
*/
@Override
public final void ll_setIntValue(int fsRef, int featureCode, int value) {
TOP fs = getFsFromId_checked(fsRef);
if (featureCode == 0) {
switchFsType(fs, value);
return;
}
FeatureImpl fi = getFeatFromCode_checked(featureCode);
if (fs._getTypeImpl().isArray()) {
throw new UnsupportedOperationException("ll_setIntValue not permitted to set a feature of an array");
}
SlotKind kind = fi.getSlotKind();
switch(kind) {
case Slot_HeapRef:
if (fi.getCode() == annotBaseSofaFeatCode) {
// setting the sofa ref of an annotationBase
// can't change this so just verify it's the same
TOP sofa = fs.getFeatureValue(fi);
if (sofa._id != value) {
throw new UnsupportedOperationException("ll_setIntValue not permitted to change a sofaRef feature");
}
return; // if the same, just ignore, already set
}
TOP ref = fs._casView.getFsFromId_checked(value);
fs.setFeatureValue(fi, ref); // does the right feature check, too
return;
case Slot_Boolean:
case Slot_Byte:
case Slot_Short:
case Slot_Int:
case Slot_Float:
fs._setIntValueCJ(fi, value);
break;
case Slot_StrRef:
String s = getStringForCode(value);
if (s == null && value != 0) {
Misc.internalError(new Exception("ll_setIntValue got null string for non-0 handle: " + value));
}
fs._setRefValueNfcCJ(fi, getStringForCode(value));
break;
case Slot_LongRef:
case Slot_DoubleRef:
Long lng = getLongForCode(value);
if (lng == null) {
Misc.internalError(new Exception("ll_setIntValue got null Long/Double for handle: " + value));
}
fs._setLongValueNfcCJ(fi, lng);
break;
default:
CASRuntimeException e = new CASRuntimeException(CASRuntimeException.INAPPROP_RANGE, fi.getName(), "int", fi.getRange().getName());
// System.err.println("debug " + e);
throw e;
}
}
private String getStringForCode(int i) {
if (null == svd.llstringSet) {
return null;
}
return svd.llstringSet.getStringForCode(i);
}
private int getCodeForString(String s) {
if (null == svd.llstringSet) {
svd.llstringSet = new StringSet();
}
return svd.llstringSet.getCodeForString(s); // avoids adding duplicates
}
private Long getLongForCode(int i) {
if (null == svd.lllongSet) {
return null;
}
return svd.lllongSet.getLongForCode(i);
}
private int getCodeForLong(long s) {
if (null == svd.lllongSet) {
svd.lllongSet = new LongSet();
}
return svd.lllongSet.getCodeForLong(s); // avoids adding duplicates
}
private void switchFsType(TOP fs, int value) {
// throw new UnsupportedOperationException();
// case where the type is being changed
// if the new type is a sub/super type of the existing type,
// some field data may be copied
// if not, no data is copied.
//
// Item is removed from index and re-indexed
// to emulate what V2 did,
// the indexing didn't change
// all the slots were the same
//
boolean wasRemoved = removeFromIndexAnyView(fs, getAddbackSingle(), FSIndexRepositoryImpl.INCLUDE_BAG_INDEXES);
if (!wasRemoved) {
svd.fsTobeAddedbackSingleInUse = false;
}
TypeImpl newType = getTypeFromCode_checked(value);
Class<?> newClass = newType.getJavaClass();
if ((fs instanceof UimaSerializable) ||
UimaSerializable.class.isAssignableFrom(newClass)) {
throw new UnsupportedOperationException("can't switch type to/from UimaSerializable");
}
// Measurement - record which type gets switched to which other type
// count how many times
// record which JCas cover class goes with each type
// key = old type, new type, old jcas cover class, new jcas cover class
// value = count
MeasureSwitchType mst = null;
if (MEASURE_SETINT) {
MeasureSwitchType key = new MeasureSwitchType(fs._getTypeImpl(), newType);
synchronized (measureSwitches) { // map access / updating must be synchronized
mst = measureSwitches.get(key);
if (null == mst) {
measureSwitches.put(key, key);
mst = key;
}
mst.count ++;
mst.newSubsumesOld = newType.subsumes(fs._getTypeImpl());
mst.oldSubsumesNew = fs._getTypeImpl().subsumes(newType);
}
}
if (newClass == fs._getTypeImpl().getJavaClass() ||
newType.subsumes(fs._getTypeImpl())) {
// switch in place
fs._setTypeImpl(newType);
return;
}
// if types don't subsume each other, we
// deviate a bit from V2 behavior
// and skip copying the feature slots
boolean isOkToCopyFeatures = // true || // debug
fs._getTypeImpl().subsumes(newType) ||
newType.subsumes(fs._getTypeImpl());
// throw new CASRuntimeException(CASRuntimeException.ILLEGAL_TYPE_CHANGE, newType.getName(), fs._getTypeImpl().getName());
TOP newFs = createFsWithExistingId(newType, fs._id); // updates id -> fs map
// initialize fields:
if (isOkToCopyFeatures) {
newFs._copyIntAndRefArraysFrom(fs);
}
// if (wasRemoved) {
// addbackSingle(newFs);
// }
// replace refs in existing FSs with new
// will miss any fs's held by user code - no way to fix that without
// universal indirection - very inefficient, so just accept for now
long st = System.nanoTime();
walkReachablePlusFSsSorted(fsItem -> {
if (fsItem._getTypeImpl().hasRefFeature) {
if (fsItem instanceof FSArray) {
TOP[] a = ((FSArray)fsItem)._getTheArray();
for (int i = 0; i < a.length; i++) {
if (fs == a[i]) {
a[i] = newFs;
}
}
return;
}
final int sz = fsItem._getTypeImpl().nbrOfUsedRefDataSlots;
for (int i = 0; i < sz; i++) {
Object o = fsItem._getRefValueCommon(i);
if (o == fs) {
fsItem._setRefValueCommon(i, newFs);
// fsItem._refData[i] = newFs;
}
}
}
},
null, // mark
null, // null or predicate for filtering what gets added
null); // null or type mapper, skips if not in other ts
if (MEASURE_SETINT) {
mst.scantime += System.nanoTime() - st;
}
}
@Override
public final void ll_setFloatValue(int fsRef, int featureCode, float value) {
getFsFromId_checked(fsRef).setFloatValue(getFeatFromCode_checked(featureCode), value);
}
// public final void ll_setFloatValueNoIndexCorruptionCheck(int fsRef, int featureCode, float value) {
// setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, float2int(value));
// }
@Override
public final void ll_setStringValue(int fsRef, int featureCode, String value) {
getFsFromId_checked(fsRef).setStringValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public final void ll_setRefValue(int fsRef, int featureCode, int value) {
// no index check because refs can't be keys
setFeatureValue(fsRef, featureCode, getFsFromId_checked(value));
}
@Override
public final void ll_setIntValue(int fsRef, int featureCode, int value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setIntValue(fsRef, featureCode, value);
}
@Override
public final void ll_setFloatValue(int fsRef, int featureCode, float value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setFloatValue(fsRef, featureCode, value);
}
@Override
public final void ll_setStringValue(int fsRef, int featureCode, String value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setStringValue(fsRef, featureCode, value);
}
@Override
public final void ll_setCharBufferValue(int fsRef, int featureCode, char[] buffer, int start,
int length, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setCharBufferValue(fsRef, featureCode, buffer, start, length);
}
@Override
public final void ll_setCharBufferValue(int fsRef, int featureCode, char[] buffer, int start,
int length) {
ll_setStringValue(fsRef, featureCode, new String(buffer, start, length));
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_copyCharBufferValue(int, int,
* char, int)
*/
@Override
public int ll_copyCharBufferValue(int fsRef, int featureCode, char[] buffer, int start) {
String str = ll_getStringValue(fsRef, featureCode);
if (str == null) {
return -1;
}
final int len = str.length();
final int requestedMax = start + len;
// Check that the buffer is long enough to copy the whole string. If it isn't long enough, we
// copy up to buffer.length - start characters.
final int max = (buffer.length < requestedMax) ? (buffer.length - start) : len;
for (int i = 0; i < max; i++) {
buffer[start + i] = str.charAt(i);
}
return len;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.impl.LowLevelCAS#ll_getCharBufferValueSize(int,
* int)
*/
@Override
public int ll_getCharBufferValueSize(int fsRef, int featureCode) {
String str = ll_getStringValue(fsRef, featureCode);
return str.length();
}
@Override
public final void ll_setRefValue(int fsRef, int featureCode, int value, boolean doTypeChecks) {
if (doTypeChecks) {
checkFsRefConditions(fsRef, featureCode);
}
ll_setRefValue(fsRef, featureCode, value);
}
public final int getIntArrayValue(IntegerArray array, int i) {
return array.get(i);
}
public final float getFloatArrayValue(FloatArray array, int i) {
return array.get(i);
}
public final String getStringArrayValue(StringArray array, int i) {
return array.get(i);
}
public final FeatureStructure getRefArrayValue(FSArray array, int i) {
return array.get(i);
}
@Override
public final int ll_getIntArrayValue(int fsRef, int position) {
return getIntArrayValue(((IntegerArray)getFsFromId_checked(fsRef)), position);
}
@Override
public final float ll_getFloatArrayValue(int fsRef, int position) {
return getFloatArrayValue(((FloatArray)getFsFromId_checked(fsRef)), position);
}
@Override
public final String ll_getStringArrayValue(int fsRef, int position) {
return getStringArrayValue(((StringArray)getFsFromId_checked(fsRef)), position);
}
@Override
public final int ll_getRefArrayValue(int fsRef, int position) {
return ((TOP)getRefArrayValue(((FSArray)getFsFromId_checked(fsRef)), position))._id();
}
private void throwAccessTypeError(int fsRef, int typeCode) {
throw new LowLevelException(LowLevelException.ACCESS_TYPE_ERROR,
Integer.valueOf(fsRef),
Integer.valueOf(typeCode),
getTypeSystemImpl().ll_getTypeForCode(typeCode).getName(),
getTypeSystemImpl().ll_getTypeForCode(ll_getFSRefType(fsRef)).getName());
}
public final void checkArrayBounds(int fsRef, int pos) {
final int arrayLength = ll_getArraySize(fsRef);
if ((pos < 0) || (pos >= arrayLength)) {
throw new ArrayIndexOutOfBoundsException(pos);
// LowLevelException e = new LowLevelException(
// LowLevelException.ARRAY_INDEX_OUT_OF_RANGE);
// e.addArgument(Integer.toString(pos));
// throw e;
}
}
public final void checkArrayBounds(int arrayLength, int pos, int length) {
if ((pos < 0) || (length < 0) || ((pos + length) > arrayLength)) {
throw new LowLevelException(LowLevelException.ARRAY_INDEX_LENGTH_OUT_OF_RANGE, Integer.toString(pos), Integer.toString(length));
}
}
/**
* Check that the fsRef is valid.
* Check that the fs is featureCode belongs to the fs
* Check that the featureCode is one of the features of the domain type of the fsRef
* feat could be primitive, string, ref to another feature
*
* @param fsRef
* @param typeCode
* @param featureCode
*/
private final void checkNonArrayConditions(int fsRef, int featureCode) {
TOP fs = getFsFromId_checked(fsRef);
final TypeImpl domainType = (TypeImpl) fs.getType();
// checkTypeAt(domType, fs); // since the type is from the FS, it's always OK
checkFeature(featureCode); // checks that the featureCode is in the range of all feature codes
TypeSystemImpl tsi = getTypeSystemImpl();
FeatureImpl fi = tsi.getFeatureForCode_checked(featureCode);
checkTypeHasFeature(domainType, fi); // checks that the feature code is one of the features of the type
// checkFsRan(fi);
}
private final void checkFsRefConditions(int fsRef, int featureCode) {
TOP fs = getFsFromId_checked(fsRef);
checkLowLevelParams(fs, fs._getTypeImpl(), featureCode); // checks type has feature
TypeSystemImpl tsi = getTypeSystemImpl();
FeatureImpl fi = tsi.getFeatureForCode_checked(featureCode);
checkFsRan(fi);
// next not needed because checkFsRan already validates this
// checkFsRef(fsRef + this.svd.casMetadata.featureOffset[featureCode]);
}
// private final void checkArrayConditions(int fsRef, int typeCode,
// int position) {
// checkTypeSubsumptionAt(fsRef, typeCode);
// // skip this next test because
// // a) it's done implicitly in the bounds check and
// // b) it fails for arrays stored outside of the main heap (e.g.,
// byteArrays, etc.)
// // checkFsRef(getArrayStartAddress(fsRef) + position);
// checkArrayBounds(fsRef, position);
// }
private final void checkPrimitiveArrayConditions(int fsRef, int typeCode, int position) {
if (typeCode != ll_getFSRefType(fsRef)) {
throwAccessTypeError(fsRef, typeCode);
}
checkArrayBounds(fsRef, position);
}
@Override
public final int ll_getIntArrayValue(int fsRef, int position, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, intArrayTypeCode, position);
}
return ll_getIntArrayValue(fsRef, position);
}
@Override
public float ll_getFloatArrayValue(int fsRef, int position, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, floatArrayTypeCode, position);
}
return ll_getFloatArrayValue(fsRef, position);
}
@Override
public String ll_getStringArrayValue(int fsRef, int position, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, stringArrayTypeCode, position);
}
return ll_getStringArrayValue(fsRef, position);
}
@Override
public int ll_getRefArrayValue(int fsRef, int position, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, fsArrayTypeCode, position);
}
return ll_getRefArrayValue(fsRef, position);
}
@Override
public void ll_setIntArrayValue(int fsRef, int position, int value, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, intArrayTypeCode, position);
}
ll_setIntArrayValue(fsRef, position, value);
}
@Override
public void ll_setFloatArrayValue(int fsRef, int position, float value, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, floatArrayTypeCode, position);
}
ll_setFloatArrayValue(fsRef, position, value);
}
@Override
public void ll_setStringArrayValue(int fsRef, int position, String value, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, stringArrayTypeCode, position);
}
ll_setStringArrayValue(fsRef, position, value);
}
@Override
public void ll_setRefArrayValue(int fsRef, int position, int value, boolean doTypeChecks) {
if (doTypeChecks) {
checkPrimitiveArrayConditions(fsRef, fsArrayTypeCode, position);
}
ll_setRefArrayValue(fsRef, position, value);
}
/* ************************
* Low Level Array Setters
* ************************/
@Override
public void ll_setIntArrayValue(int fsRef, int position, int value) {
IntegerArray array = getFsFromId_checked(fsRef);
array.set(position, value); // that set operation does required journaling
}
@Override
public void ll_setFloatArrayValue(int fsRef, int position, float value) {
FloatArray array = getFsFromId_checked(fsRef);
array.set(position, value); // that set operation does required journaling
}
@Override
public void ll_setStringArrayValue(int fsRef, int position, String value) {
StringArray array = getFsFromId_checked(fsRef);
array.set(position, value); // that set operation does required journaling
}
@Override
public void ll_setRefArrayValue(int fsRef, int position, int value) {
FSArray array = getFsFromId_checked(fsRef);
array.set(position, getFsFromId_checked(value)); // that set operation does required journaling
}
/**
* @param fsRef an id for a FS
* @return the type code for this FS
*/
@Override
public int ll_getFSRefType(int fsRef) {
return getFsFromId_checked(fsRef)._getTypeCode();
}
@Override
public int ll_getFSRefType(int fsRef, boolean doChecks) {
// type code is always valid
return ll_getFSRefType(fsRef);
}
@Override
public LowLevelCAS getLowLevelCAS() {
return this;
}
@Override
public int size() {
throw new UIMARuntimeException(UIMARuntimeException.INTERNAL_ERROR);
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.admin.CASMgr#getJCasClassLoader()
*/
@Override
public ClassLoader getJCasClassLoader() {
return this.svd.jcasClassLoader;
}
/*
* Called to set the overall jcas class loader to use.
*
* @see org.apache.uima.cas.admin.CASMgr#setJCasClassLoader(java.lang.ClassLoader)
*/
@Override
public void setJCasClassLoader(ClassLoader classLoader) {
this.svd.jcasClassLoader = classLoader;
}
// Internal use only, public for cross package use
// Assumes: The JCasClassLoader for a CAS is set up initially when the CAS is
// created
// and not switched (other than by this code) once it is set.
// Callers of this method always code the "restoreClassLoaderUnlockCAS" in
// pairs,
// protected as needed with try - finally blocks.
//
// Special handling is needed for CAS Mulipliers - they can modify a cas up to
// the point they no longer "own" it.
// So the try / finally approach doesn't fit
public void switchClassLoaderLockCas(Object userCode) {
switchClassLoaderLockCasCL(userCode.getClass().getClassLoader());
}
public void switchClassLoaderLockCasCL(ClassLoader newClassLoader) {
// lock out CAS functions to which annotator should not have access
enableReset(false);
svd.switchClassLoader(newClassLoader);
}
// // internal use, public for cross-package ref
// public boolean usingBaseClassLoader() {
// return (this.svd.jcasClassLoader == this.svd.previousJCasClassLoader);
// }
public void restoreClassLoaderUnlockCas() {
// unlock CAS functions
enableReset(true);
// this might be called without the switch ever being called
svd.restoreClassLoader();
}
@Override
public FeatureValuePath createFeatureValuePath(String featureValuePath)
throws CASRuntimeException {
return FeatureValuePathImpl.getFeaturePath(featureValuePath);
}
@Override
public void setOwner(CasOwner aCasOwner) {
CASImpl baseCas = getBaseCAS();
if (baseCas != this) {
baseCas.setOwner(aCasOwner);
} else {
super.setOwner(aCasOwner);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.AbstractCas_ImplBase#release()
*/
@Override
public void release() {
CASImpl baseCas = getBaseCAS();
if (baseCas != this) {
baseCas.release();
} else {
super.release();
}
}
/* **********************************
* A R R A Y C R E A T I O N
************************************/
@Override
public ByteArrayFS createByteArrayFS(int length) throws CASRuntimeException {
checkArrayPreconditions(length);
return new ByteArray(this.getJCas(), length);
}
@Override
public BooleanArrayFS createBooleanArrayFS(int length) throws CASRuntimeException {
checkArrayPreconditions(length);
return new BooleanArray(this.getJCas(), length);
}
@Override
public ShortArrayFS createShortArrayFS(int length) throws CASRuntimeException {
checkArrayPreconditions(length);
return new ShortArray(this.getJCas(), length);
}
@Override
public LongArrayFS createLongArrayFS(int length) throws CASRuntimeException {
checkArrayPreconditions(length);
return new LongArray(this.getJCas(), length);
}
@Override
public DoubleArrayFS createDoubleArrayFS(int length) throws CASRuntimeException {
checkArrayPreconditions(length);
return new DoubleArray(this.getJCas(), length);
}
@Override
public byte ll_getByteValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getByteValue(getFeatFromCode_checked(featureCode));
}
@Override
public byte ll_getByteValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getByteValue(fsRef, featureCode);
}
@Override
public boolean ll_getBooleanValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getBooleanValue(getFeatFromCode_checked(featureCode));
}
@Override
public boolean ll_getBooleanValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getBooleanValue(fsRef, featureCode);
}
@Override
public short ll_getShortValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getShortValue(getFeatFromCode_checked(featureCode));
}
@Override
public short ll_getShortValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getShortValue(fsRef, featureCode);
}
// impossible to implement in v3; change callers
// public long ll_getLongValue(int offset) {
// return this.getLongHeap().getHeapValue(offset);
// }
@Override
public long ll_getLongValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getLongValue(getFeatFromCode_checked(featureCode));
}
// public long ll_getLongValueFeatOffset(int fsRef, int offset) {
// TOP fs = getFsFromId_checked(fsRef);
// return fs.getLongValueOffset(offset);
// }
@Override
public long ll_getLongValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getLongValue(fsRef, featureCode);
}
@Override
public double ll_getDoubleValue(int fsRef, int featureCode) {
return getFsFromId_checked(fsRef).getDoubleValue(getFeatFromCode_checked(featureCode));
}
// public double ll_getDoubleValueFeatOffset(int fsRef, int offset) {
// TOP fs = getFsFromId_checked(fsRef);
// return fs.getDoubleValueOffset(offset);
// }
@Override
public double ll_getDoubleValue(int fsRef, int featureCode, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
return ll_getDoubleValue(fsRef, featureCode);
}
@Override
public void ll_setBooleanValue(int fsRef, int featureCode, boolean value) {
getFsFromId_checked(fsRef).setBooleanValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public void ll_setBooleanValue(int fsRef, int featureCode, boolean value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setBooleanValue(fsRef, featureCode, value);
}
@Override
public final void ll_setByteValue(int fsRef, int featureCode, byte value) {
getFsFromId_checked(fsRef).setByteValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public void ll_setByteValue(int fsRef, int featureCode, byte value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setByteValue(fsRef, featureCode, value);
}
@Override
public final void ll_setShortValue(int fsRef, int featureCode, short value) {
getFsFromId_checked(fsRef).setShortValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public void ll_setShortValue(int fsRef, int featureCode, short value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setShortValue(fsRef, featureCode, value);
}
@Override
public void ll_setLongValue(int fsRef, int featureCode, long value) {
getFsFromId_checked(fsRef).setLongValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public void ll_setLongValue(int fsRef, int featureCode, long value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setLongValue(fsRef, featureCode, value);
}
@Override
public void ll_setDoubleValue(int fsRef, int featureCode, double value) {
getFsFromId_checked(fsRef).setDoubleValue(getFeatFromCode_checked(featureCode), value);
}
@Override
public void ll_setDoubleValue(int fsRef, int featureCode, double value, boolean doTypeChecks) {
if (doTypeChecks) {
checkNonArrayConditions(fsRef, featureCode);
}
ll_setDoubleValue(fsRef, featureCode, value);
}
@Override
public byte ll_getByteArrayValue(int fsRef, int position) {
return ((ByteArray) getFsFromId_checked(fsRef)).get(position);
}
@Override
public byte ll_getByteArrayValue(int fsRef, int position, boolean doTypeChecks) {
return ll_getByteArrayValue(fsRef, position);
}
@Override
public boolean ll_getBooleanArrayValue(int fsRef, int position) {
return ((BooleanArray) getFsFromId_checked(fsRef)).get(position);
}
@Override
public boolean ll_getBooleanArrayValue(int fsRef, int position, boolean doTypeChecks) {
return ll_getBooleanArrayValue(fsRef, position);
}
@Override
public short ll_getShortArrayValue(int fsRef, int position) {
return ((ShortArray) getFsFromId_checked(fsRef)).get(position);
}
@Override
public short ll_getShortArrayValue(int fsRef, int position, boolean doTypeChecks) {
return ll_getShortArrayValue(fsRef, position);
}
@Override
public long ll_getLongArrayValue(int fsRef, int position) {
return ((LongArray) getFsFromId_checked(fsRef)).get(position);
}
@Override
public long ll_getLongArrayValue(int fsRef, int position, boolean doTypeChecks) {
return ll_getLongArrayValue(fsRef, position);
}
@Override
public double ll_getDoubleArrayValue(int fsRef, int position) {
return ((DoubleArray) getFsFromId_checked(fsRef)).get(position);
}
@Override
public double ll_getDoubleArrayValue(int fsRef, int position, boolean doTypeChecks) {
return ll_getDoubleArrayValue(fsRef, position);
}
@Override
public void ll_setByteArrayValue(int fsRef, int position, byte value) {
((ByteArray) getFsFromId_checked(fsRef)).set(position, value);
}
@Override
public void ll_setByteArrayValue(int fsRef, int position, byte value, boolean doTypeChecks) {
ll_setByteArrayValue(fsRef, position, value);}
@Override
public void ll_setBooleanArrayValue(int fsRef, int position, boolean b) {
((BooleanArray) getFsFromId_checked(fsRef)).set(position, b);
}
@Override
public void ll_setBooleanArrayValue(int fsRef, int position, boolean value, boolean doTypeChecks) {
ll_setBooleanArrayValue(fsRef, position, value);
}
@Override
public void ll_setShortArrayValue(int fsRef, int position, short value) {
((ShortArray) getFsFromId_checked(fsRef)).set(position, value);
}
@Override
public void ll_setShortArrayValue(int fsRef, int position, short value, boolean doTypeChecks) {
ll_setShortArrayValue(fsRef, position, value);
}
@Override
public void ll_setLongArrayValue(int fsRef, int position, long value) {
((LongArray) getFsFromId_checked(fsRef)).set(position, value);
}
@Override
public void ll_setLongArrayValue(int fsRef, int position, long value, boolean doTypeChecks) {
ll_setLongArrayValue(fsRef, position, value);
}
@Override
public void ll_setDoubleArrayValue(int fsRef, int position, double d) {
((DoubleArray) getFsFromId_checked(fsRef)).set(position, d);
}
@Override
public void ll_setDoubleArrayValue(int fsRef, int position, double value, boolean doTypeChecks) {
ll_setDoubleArrayValue(fsRef, position, value);
}
public boolean isAnnotationType(Type t) {
return ((TypeImpl)t).isAnnotationType();
}
/**
* @param t the type code to test
* @return true if that type is subsumed by AnnotationBase type
*/
public boolean isSubtypeOfAnnotationBaseType(int t) {
TypeImpl ti = getTypeFromCode(t);
return (ti == null) ? false : ti.isAnnotationBaseType();
}
public boolean isBaseCas() {
return this == getBaseCAS();
}
@Override
public Annotation createAnnotation(Type type, int begin, int end) {
// duplicates a later check
// if (this.isBaseCas()) {
// // Can't create annotation on base CAS
// throw new CASRuntimeException(CASRuntimeException.INVALID_BASE_CAS_METHOD, "createAnnotation(Type, int, int)");
// }
Annotation fs = (Annotation) createFS(type);
fs.setBegin(begin);
fs.setEnd(end);
return fs;
}
public int ll_createAnnotation(int typeCode, int begin, int end) {
TOP fs = createAnnotation(getTypeFromCode(typeCode), begin, end);
svd.id2fs.put(fs); // to prevent gc from reclaiming
return fs._id();
}
/**
* The generic spec T extends AnnotationFS (rather than AnnotationFS) allows the method
* JCasImpl getAnnotationIndex to return Annotation instead of AnnotationFS
* @param <T> the Java class associated with the annotation index
* @return the annotation index
*/
@Override
public <T extends AnnotationFS> AnnotationIndex<T> getAnnotationIndex() {
return (AnnotationIndex<T>) indexRepository.getAnnotationIndex(getTypeSystemImpl().annotType);
}
/* (non-Javadoc)
* @see org.apache.uima.cas.CAS#getAnnotationIndex(org.apache.uima.cas.Type)
*/
@Override
public <T extends AnnotationFS> AnnotationIndex<T> getAnnotationIndex(Type type)
throws CASRuntimeException {
return (AnnotationIndex<T>) indexRepository.getAnnotationIndex((TypeImpl) type);
}
/**
* @see org.apache.uima.cas.CAS#getAnnotationType()
*/
@Override
public Type getAnnotationType() {
return getTypeSystemImpl().annotType;
}
/**
* @see org.apache.uima.cas.CAS#getEndFeature()
*/
@Override
public Feature getEndFeature() {
return getTypeSystemImpl().endFeat;
}
/**
* @see org.apache.uima.cas.CAS#getBeginFeature()
*/
@Override
public Feature getBeginFeature() {
return getTypeSystemImpl().startFeat;
}
private <T extends AnnotationFS> T createDocumentAnnotation(int length) {
final TypeSystemImpl ts = getTypeSystemImpl();
// Remove any existing document annotations.
FSIterator<T> it = this.<T>getAnnotationIndex(ts.docType).iterator();
List<T> list = new ArrayList<T>();
while (it.isValid()) {
list.add(it.get());
it.moveToNext();
}
for (int i = 0; i < list.size(); i++) {
getIndexRepository().removeFS(list.get(i));
}
return (T) createDocumentAnnotationNoRemove(length);
}
private <T extends Annotation> T createDocumentAnnotationNoRemove(int length) {
T docAnnot = createDocumentAnnotationNoRemoveNoIndex(length);
addFsToIndexes(docAnnot);
return docAnnot;
}
public <T extends Annotation> T createDocumentAnnotationNoRemoveNoIndex(int length) {
final TypeSystemImpl ts = getTypeSystemImpl();
AnnotationFS docAnnot = createAnnotation(ts.docType, 0, length);
docAnnot.setStringValue(ts.langFeat, CAS.DEFAULT_LANGUAGE_NAME);
return (T) docAnnot;
}
public int ll_createDocumentAnnotation(int length) {
final int fsRef = ll_createDocumentAnnotationNoIndex(0, length);
ll_getIndexRepository().ll_addFS(fsRef);
return fsRef;
}
public int ll_createDocumentAnnotationNoIndex(int begin, int end) {
final TypeSystemImpl ts = getTypeSystemImpl();
int fsRef = ll_createAnnotation(ts.docType.getCode(), begin, end);
ll_setStringValue(fsRef, ts.langFeat.getCode(), CAS.DEFAULT_LANGUAGE_NAME);
return fsRef;
}
// For the "built-in" instance of Document Annotation, set the
// "end" feature to be the length of the sofa string
/**
* updates the document annotation (only if the sofa's local string data != null)
* setting the end feature to be the length of the sofa string, if any.
* creates the document annotation if not present
* only works if not in the base cas
*
*/
public void updateDocumentAnnotation() {
if (!mySofaIsValid() || this == this.svd.baseCAS) {
return;
}
String newDoc = this.mySofaRef.getLocalStringData();
if (null != newDoc) {
Annotation docAnnot = getDocumentAnnotationNoCreate();
if (docAnnot != null) {
// use a local instance of the add-back memory because this may be called as a side effect of updating a sofa
FSsTobeAddedback tobeAddedback = FSsTobeAddedback.createSingle();
boolean wasRemoved = this.checkForInvalidFeatureSetting(
docAnnot, getTypeSystemImpl().endFeat.getCode(), tobeAddedback);
docAnnot._setIntValueNfc(endFeatAdjOffset, newDoc.length());
if (wasRemoved) {
tobeAddedback.addback(docAnnot);
}
} else {
// not in the index (yet)
createDocumentAnnotation(newDoc.length());
}
}
return;
}
/**
* Generic issue: The returned document annotation could be either an instance of
* DocumentAnnotation or an instance of Annotation - the Java cover class used for
* annotations when JCas is not being used.
*/
@Override
public <T extends AnnotationFS> T getDocumentAnnotation() {
T docAnnot = (T) getDocumentAnnotationNoCreate();
if (null == docAnnot) {
return (T) createDocumentAnnotationNoRemove(0);
} else {
return docAnnot;
}
}
public <T extends AnnotationFS> T getDocumentAnnotationNoCreate() {
if (this == this.svd.baseCAS) {
// base CAS has no document
return null;
}
FSIterator<T> it = this.<T>getAnnotationIndex(getTypeSystemImpl().docType).iterator();
if (it.isValid()) {
return it.get();
}
return null;
}
/**
*
* @return the fs addr of the document annotation found via the index, or 0 if not there
*/
public int ll_getDocumentAnnotation() {
if (this == this.svd.baseCAS) {
// base CAS has no document
return 0;
}
FSIterator<FeatureStructure> it = getIndexRepository().getIndex(CAS.STD_ANNOTATION_INDEX, getTypeSystemImpl().docType).iterator();
if (it.isValid()) {
return it.get()._id();
}
return 0;
}
@Override
public String getDocumentLanguage() {
if (this == this.svd.baseCAS) {
// base CAS has no document
return null;
}
return getDocumentAnnotation().getStringValue(getTypeSystemImpl().langFeat);
}
@Override
public String getDocumentText() {
return this.getSofaDataString();
}
@Override
public String getSofaDataString() {
if (this == this.svd.baseCAS) {
// base CAS has no document
return null;
}
return mySofaIsValid() ? mySofaRef.getLocalStringData() : null;
}
@Override
public FeatureStructure getSofaDataArray() {
if (this == this.svd.baseCAS) {
// base CAS has no Sofa
return null;
}
return mySofaIsValid() ? mySofaRef.getLocalFSData() : null;
}
@Override
public String getSofaDataURI() {
if (this == this.svd.baseCAS) {
// base CAS has no Sofa
return null;
}
return mySofaIsValid() ? mySofaRef.getSofaURI() : null;
}
@Override
public InputStream getSofaDataStream() {
if (this == this.svd.baseCAS) {
// base CAS has no Sofa nothin
return null;
}
// return mySofaRef.getSofaDataStream(); // this just goes to the next method
return mySofaIsValid() ? this.getSofaDataStream(mySofaRef) : null;
}
@Override
public String getSofaMimeType() {
if (this == this.svd.baseCAS) {
// base CAS has no Sofa
return null;
}
return mySofaIsValid() ? mySofaRef.getSofaMime() : null;
}
@Override
public Sofa getSofa() {
return mySofaRef;
}
/**
* @return the addr of the sofaFS associated with this view, or 0
*/
@Override
public int ll_getSofa() {
return mySofaIsValid() ? mySofaRef._id() : 0;
}
@Override
public String getViewName() {
return (this == getViewFromSofaNbr(1)) ? CAS.NAME_DEFAULT_SOFA :
mySofaIsValid() ? mySofaRef.getSofaID() :
null;
}
private boolean mySofaIsValid() {
return this.mySofaRef != null;
}
void setDocTextFromDeserializtion(String text) {
if (mySofaIsValid()) {
SofaFS sofa = getSofaRef(); // creates sofa if doesn't already exist
sofa.setLocalSofaData(text);
}
}
@Override
public void setDocumentLanguage(String languageCode) {
if (this == this.svd.baseCAS) {
throw new CASRuntimeException(CASRuntimeException.INVALID_BASE_CAS_METHOD, "setDocumentLanguage(String)");
}
Annotation docAnnot = getDocumentAnnotation();
FeatureImpl languageFeature = getTypeSystemImpl().langFeat;
languageCode = Language.normalize(languageCode);
boolean wasRemoved = this.checkForInvalidFeatureSetting(docAnnot, languageFeature.getCode(), this.getAddbackSingle());
docAnnot.setStringValue(getTypeSystemImpl().langFeat, languageCode);
addbackSingleIfWasRemoved(wasRemoved, docAnnot);
}
private void setSofaThingsMime(Consumer<Sofa> c, String msg) {
if (this == this.svd.baseCAS) {
throw new CASRuntimeException(CASRuntimeException.INVALID_BASE_CAS_METHOD, msg);
}
Sofa sofa = getSofaRef();
c.accept(sofa);
}
@Override
public void setDocumentText(String text) {
setSofaDataString(text, "text");
}
@Override
public void setSofaDataString(String text, String mime) throws CASRuntimeException {
setSofaThingsMime(sofa -> sofa.setLocalSofaData(text, mime), "setSofaDataString(text, mime)");
}
@Override
public void setSofaDataArray(FeatureStructure array, String mime) {
setSofaThingsMime(sofa -> sofa.setLocalSofaData(array, mime), "setSofaDataArray(FeatureStructure, mime)");
}
@Override
public void setSofaDataURI(String uri, String mime) throws CASRuntimeException {
setSofaThingsMime(sofa -> sofa.setRemoteSofaURI(uri, mime), "setSofaDataURI(String, String)");
}
@Override
public void setCurrentComponentInfo(ComponentInfo info) {
// always store component info in base CAS
this.svd.componentInfo = info;
}
ComponentInfo getCurrentComponentInfo() {
return this.svd.componentInfo;
}
/**
* @see org.apache.uima.cas.CAS#addFsToIndexes(FeatureStructure fs)
*/
@Override
public void addFsToIndexes(FeatureStructure fs) {
// if (fs instanceof AnnotationBaseFS) {
// final CAS sofaView = ((AnnotationBaseFS) fs).getView();
// if (sofaView != this) {
// CASRuntimeException e = new CASRuntimeException(
// CASRuntimeException.ANNOTATION_IN_WRONG_INDEX, new String[] { fs.toString(),
// sofaView.getSofa().getSofaID(), this.getSofa().getSofaID() });
// throw e;
// }
// }
this.indexRepository.addFS(fs);
}
/**
* @see org.apache.uima.cas.CAS#removeFsFromIndexes(FeatureStructure fs)
*/
@Override
public void removeFsFromIndexes(FeatureStructure fs) {
this.indexRepository.removeFS(fs);
}
/**
* @param fs the AnnotationBase instance
* @return the view associated with this FS where it could be indexed
*/
public CASImpl getSofaCasView(AnnotationBase fs) {
return fs._casView;
// Sofa sofa = fs.getSofa();
//
// if (null != sofa && sofa != this.getSofa()) {
// return (CASImpl) this.getView(sofa.getSofaNum());
// }
//
// /* Note: sofa == null means annotation created from low-level APIs, without setting sofa feature
// * Ignore this for backwards compatibility */
// return this;
}
@Override
public CASImpl ll_getSofaCasView(int id) {
return getSofaCasView(getFsFromId_checked(id));
}
// public Iterator<CAS> getViewIterator() {
// List<CAS> viewList = new ArrayList<CAS>();
// // add initial view if it has no sofa
// if (!((CASImpl) getInitialView()).mySofaIsValid()) {
// viewList.add(getInitialView());
// }
// // add views with Sofas
// FSIterator<SofaFS> sofaIter = getSofaIterator();
// while (sofaIter.hasNext()) {
// viewList.add(getView(sofaIter.next()));
// }
// return viewList.iterator();
// }
/**
* Creates the initial view (without a sofa) if not present
* @return the number of views, excluding the base view, including the initial view (even if not initially present or no sofa)
*/
public int getNumberOfViews() {
CASImpl initialView = getInitialView(); // creates one if not existing, w/o sofa
int nbrSofas = this.svd.baseCAS.indexRepository.getIndex(CAS.SOFA_INDEX_NAME).size();
return initialView.mySofaIsValid() ? nbrSofas : 1 + nbrSofas;
}
public int getNumberOfSofas() {
return this.svd.baseCAS.indexRepository.getIndex(CAS.SOFA_INDEX_NAME).size();
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.CAS#getViewIterator()
*/
@Override
public <T extends CAS> Iterator<T> getViewIterator() {
return new Iterator<T>() {
final CASImpl initialView = getInitialView(); // creates one if not existing, w/o sofa
boolean isInitialView_but_noSofa = !initialView.mySofaIsValid(); // true if has no Sofa in initial view
// but is reset to false once iterator moves
// off of initial view.
// if initial view has a sofa, we just use the
// sofa iterator instead.
final FSIterator<Sofa> sofaIter = getSofaIterator();
@Override
public boolean hasNext() {
if (isInitialView_but_noSofa) {
return true;
}
return sofaIter.hasNext();
}
@Override
public T next() {
if (isInitialView_but_noSofa) {
isInitialView_but_noSofa = false; // no incr of sofa iterator because it was missing initial view
return (T) initialView;
}
return (T) getView(sofaIter.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* excludes initial view if its sofa is not valid
*
* @return iterator over all views except the base view
*/
public Iterator<CASImpl> getViewImplIterator() {
return new Iterator<CASImpl>() {
final CASImpl initialView = getInitialView(); // creates one if not existing, w/o sofa
boolean isInitialView_but_noSofa = !initialView.mySofaIsValid(); // true if has no Sofa in initial view
// but is reset to false once iterator moves
// off of initial view.
// if initial view has a sofa, we just use the
// sofa iterator instead.
final FSIterator<Sofa> sofaIter = getSofaIterator();
@Override
public boolean hasNext() {
if (isInitialView_but_noSofa) { // set to false once iterator moves off of first value
return true;
}
return sofaIter.hasNext();
}
@Override
public CASImpl next() {
if (isInitialView_but_noSofa) {
isInitialView_but_noSofa = false; // no incr of sofa iterator because it was missing initial view
return initialView;
}
return getView(sofaIter.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* iterate over all views in view order (by view number)
* @param processViews
*/
void forAllViews(Consumer<CASImpl> processViews) {
final int numViews = this.getNumberOfViews();
for (int viewNbr = 1; viewNbr <= numViews; viewNbr++) {
CASImpl view = (viewNbr == 1) ? getInitialView() : (CASImpl) getView(viewNbr);
processViews.accept(view);
}
//
// Iterator<CASImpl> it = getViewImplIterator();
// while (it.hasNext()) {
// processViews.accept(it.next());
// }
}
void forAllSofas(Consumer<Sofa> processSofa) {
FSIterator<Sofa> it = getSofaIterator();
while (it.hasNext()) {
processSofa.accept(it.nextNvc());
}
}
/**
* Excludes base view's ir,
* Includes the initial view's ir only if it has a sofa defined
* @param processIr the code to execute
*/
void forAllIndexRepos(Consumer<FSIndexRepositoryImpl> processIr) {
final int numViews = this.getViewCount();
for (int viewNum = 1; viewNum <= numViews; viewNum++) {
processIr.accept(this.getSofaIndexRepository(viewNum));
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.CAS#getViewIterator(java.lang.String)
*/
@Override
public Iterator<CAS> getViewIterator(String localViewNamePrefix) {
// do sofa mapping for current component
String absolutePrefix = null;
if (getCurrentComponentInfo() != null) {
absolutePrefix = getCurrentComponentInfo().mapToSofaID(localViewNamePrefix);
}
if (absolutePrefix == null) {
absolutePrefix = localViewNamePrefix;
}
// find Sofas with this prefix
List<CAS> viewList = new ArrayList<CAS>();
FSIterator<Sofa> sofaIter = getSofaIterator();
while (sofaIter.hasNext()) {
SofaFS sofa = sofaIter.next();
String sofaId = sofa.getSofaID();
if (sofaId.startsWith(absolutePrefix)) {
if ((sofaId.length() == absolutePrefix.length())
|| (sofaId.charAt(absolutePrefix.length()) == '.')) {
viewList.add(getView(sofa));
}
}
}
return viewList.iterator();
}
/**
* protectIndexes
*
* Within the scope of protectIndexes,
* feature updates are checked, and if found to be a key, and the FS is in a corruptable index,
* then the FS is removed from the indexes (in all necessary views) (perhaps multiple times
* if the FS was added to the indexes multiple times), and this removal is recorded on
* an new instance of FSsTobeReindexed appended to fssTobeAddedback.
*
* Later, when the protectIndexes is closed, the tobe items are added back to the indies.
*/
@Override
public AutoCloseable protectIndexes() {
FSsTobeAddedback r = FSsTobeAddedback.createMultiple(this);
svd.fssTobeAddedback.add(r);
return r;
}
void dropProtectIndexesLevel () {
svd.fssTobeAddedback.remove(svd.fssTobeAddedback.size() -1);
}
/**
* This design is to support normal operations where the
* addbacks could be nested
* It also handles cases where nested ones were inadvertently left open
* Three cases:
* 1) the addbacks are the last element in the stack
* - remove it from the stack
* 2) the addbacks are (no longer) in the list
* - leave stack alone
* 3) the addbacks are in the list but not at the end
* - remove it and all later ones
*
* If the "withProtectedindexes" approach is used, it guarantees proper
* nesting, but the Runnable can't throw checked exceptions.
*
* You can do your own try-finally blocks (or use the try with resources
* form in Java 8 to do a similar thing with no restrictions on what the
* body can contain.
*
* @param addbacks
*/
void addbackModifiedFSs (FSsTobeAddedback addbacks) {
final List<FSsTobeAddedback> listOfAddbackInfos = svd.fssTobeAddedback;
if (listOfAddbackInfos.get(listOfAddbackInfos.size() - 1) == addbacks) {
listOfAddbackInfos.remove(listOfAddbackInfos.size());
} else {
int pos = listOfAddbackInfos.indexOf(addbacks);
if (pos >= 0) {
for (int i = listOfAddbackInfos.size() - 1; i > pos; i--) {
FSsTobeAddedback toAddBack = listOfAddbackInfos.remove(i);
toAddBack.addback();
}
}
}
addbacks.addback();
}
/**
*
* @param r an inner block of code to be run with
*/
@Override
public void protectIndexes(Runnable r) {
AutoCloseable addbacks = protectIndexes();
try {
r.run();
} finally {
addbackModifiedFSs((FSsTobeAddedback) addbacks);
}
}
/**
* The current implementation only supports 1 marker call per
* CAS. Subsequent calls will throw an error.
*
* The design is intended to support (at some future point)
* multiple markers; for this to work, the intent is to
* extend the MarkerImpl to keep track of indexes into
* these IntVectors specifying where that marker starts/ends.
*/
@Override
public Marker createMarker() {
if (!this.svd.flushEnabled) {
throw new CASAdminException(CASAdminException.FLUSH_DISABLED);
}
this.svd.trackingMark = new MarkerImpl(this.getLastUsedFsId() + 1,
this);
if (this.svd.modifiedPreexistingFSs == null) {
this.svd.modifiedPreexistingFSs = new IdentityHashMap<>();
}
if (this.svd.modifiedPreexistingFSs.size() > 0) {
errorMultipleMarkers();
}
if (this.svd.trackingMarkList == null) {
this.svd.trackingMarkList = new ArrayList<MarkerImpl>();
} else {errorMultipleMarkers();}
this.svd.trackingMarkList.add(this.svd.trackingMark);
return this.svd.trackingMark;
}
private void errorMultipleMarkers() {
throw new CASRuntimeException(CASRuntimeException.MULTIPLE_CREATE_MARKER);
}
// made public https://issues.apache.org/jira/browse/UIMA-2478
public MarkerImpl getCurrentMark() {
return this.svd.trackingMark;
}
/**
*
* @return an array of FsChange items, one per modified Fs,
* sorted in order of fs._id
*/
FsChange[] getModifiedFSList() {
final Map<TOP, FsChange> mods = this.svd.modifiedPreexistingFSs;
FsChange[] r = mods.values().toArray(new FsChange[mods.size()]);
Arrays.sort(r, 0, mods.size(), (c1, c2) -> Integer.compare(c1.fs._id, c2.fs._id));
return r;
}
boolean isInModifiedPreexisting(TOP fs) {
return this.svd.modifiedPreexistingFSs.containsKey(fs);
}
@Override
public String toString() {
String sofa = (mySofaRef == null)
? (isBaseCas()
? "Base CAS"
: "_InitialView or no Sofa")
: mySofaRef.getSofaID();
// (mySofaRef == 0) ? "no Sofa" :
return this.getClass().getSimpleName() + ":" + getCasId() + "[view: " + sofa + "]";
}
int getCasResets() {
return svd.casResets.get();
}
int getCasId() {
return svd.casId;
}
final public int getNextFsId(TOP fs) {
return svd.getNextFsId(fs);
}
public void adjustLastFsV2size(int arrayLength) {
svd.lastFsV2Size += 1 + arrayLength; // 1 is for array length value
}
/**
* Test case use
* @param fss the FSs to include in the id 2 fs map
*/
public void setId2FSs(FeatureStructure ... fss) {
for (FeatureStructure fs : fss) {
if (IS_ALWAYS_HOLD_ONTO_FSS) {
svd.id2fs.putUnconditionally((TOP)fs);
} else {
svd.id2fs.put((TOP)fs);
}
}
}
// Not currently used
// public Int2ObjHashMap<TOP> getId2FSs() {
// return svd.id2fs.getId2fs();
// }
// final private int getNextFsId() {
// return ++ svd.fsIdGenerator;
// }
final public int getLastUsedFsId() {
return svd.fsIdGenerator;
}
/**
* Call this to capture the current value of fsIdGenerator and make it
* available to other threads.
*
* Must be called on a thread that has been synchronized with the thread used for creating FSs for this CAS.
*/
final public void captureLastFsIdForOtherThread() {
svd.fsIdLastValue.set(svd.fsIdGenerator);
}
public <T extends TOP> T getFsFromId(int id) {
return (T) this.svd.id2fs.get(id);
}
// /**
// * plus means all reachable, plus maybe others not reachable but not yet gc'd
// * @param action -
// */
// public void walkReachablePlusFSsSorted(Consumer<TOP> action) {
// this.svd.id2fs.walkReachablePlusFSsSorted(action);
// }
// /**
// * called for delta serialization - walks just the new items above the line
// * @param action -
// * @param fromId - the id of the first item to walk from
// */
// public void walkReachablePlusFSsSorted(Consumer<TOP> action, int fromId) {
// this.svd.id2fs.walkReachablePlueFSsSorted(action, fromId);
// }
/**
* find all of the FSs via the indexes plus what's reachable.
* sort into order by id,
*
* Apply the action to those
* Return the list of sorted FSs
*
* @param action_filtered action to perform on each item after filtering
* @param mark null or the mark
* @param includeFilter null or a filter (exclude items not in other type system)
* @param typeMapper null or how to map to other type system, used to skip things missing in other type system
* @return sorted list of all found items (ignoring mark)
*/
public List<TOP> walkReachablePlusFSsSorted(
Consumer<TOP> action_filtered, MarkerImpl mark, Predicate<TOP> includeFilter, CasTypeSystemMapper typeMapper) {
List<TOP> all = new AllFSs(this, mark, includeFilter, typeMapper).getAllFSsSorted();
List<TOP> filtered = filterAboveMark(all, mark);
for (TOP fs : filtered) {
action_filtered.accept(fs);
}
return all;
}
static List<TOP> filterAboveMark(List<TOP> all, MarkerImpl mark) {
if (null == mark) {
return all;
}
int c = Collections.binarySearch(all, TOP._createSearchKey(mark.nextFSId),
(fs1, fs2) -> Integer.compare(fs1._id, fs2._id));
if (c < 0) {
c = (-c) - 1;
}
return all.subList(c, all.size());
}
// /**
// * Get the Java class corresponding to a particular type
// * Only valid after type system commit
// *
// * @param type
// * @return
// */
// public <T extends FeatureStructure> Class<T> getClass4Type(Type type) {
// TypeSystemImpl tsi = getTypeSystemImpl();
// if (!tsi.isCommitted()) {
// throw new CASRuntimeException(CASRuntimeException.GET_CLASS_FOR_TYPE_BEFORE_TS_COMMIT);
// }
//
// }
public static boolean isSameCAS(CAS c1, CAS c2) {
CASImpl ci1 = (CASImpl) c1.getLowLevelCAS();
CASImpl ci2 = (CASImpl) c2.getLowLevelCAS();
return ci1.getBaseCAS() == ci2.getBaseCAS();
}
public boolean isInCAS(FeatureStructure fs) {
return ((TOP)fs)._casView.getBaseCAS() == this.getBaseCAS();
}
// /**
// *
// * @param typecode -
// * @return Object that can be cast to either a 2 or 3 arg createFs functional interface
// * FsGenerator or FsGeneratorArray
// */
// private Object getFsGenerator(int typecode) {
// return getTypeSystemImpl().getGenerator(typecode);
// }
public final void checkArrayPreconditions(int len) throws CASRuntimeException {
// Check array size.
if (len < 0) {
throw new CASRuntimeException(CASRuntimeException.ILLEGAL_ARRAY_SIZE);
}
}
public EmptyFSList getEmptyFSList() {
if (null == svd.emptyFSList) {
svd.emptyFSList = new EmptyFSList(getTypeSystemImpl().fsEListType, this);
}
return svd.emptyFSList;
}
public EmptyFloatList getEmptyFloatList() {
if (null == svd.emptyFloatList) {
svd.emptyFloatList = new EmptyFloatList(getTypeSystemImpl().floatEListType, this);
}
return svd.emptyFloatList;
}
public EmptyIntegerList getEmptyIntegerList() {
if (null == svd.emptyIntegerList) {
svd.emptyIntegerList = new EmptyIntegerList(getTypeSystemImpl().intEListType, this);
}
return svd.emptyIntegerList;
}
public EmptyStringList getEmptyStringList() {
if (null == svd.emptyStringList) {
svd.emptyStringList = new EmptyStringList(getTypeSystemImpl().stringEListType, this);
}
return svd.emptyStringList;
}
/**
* @param rangeCode special codes for serialization use only
* @return the empty list (shared) corresponding to the type
*/
public EmptyList getEmptyList(int rangeCode) {
return (rangeCode == CasSerializerSupport.TYPE_CLASS_INTLIST) ? getEmptyIntegerList() :
(rangeCode == CasSerializerSupport.TYPE_CLASS_FLOATLIST) ? getEmptyFloatList() :
(rangeCode == CasSerializerSupport.TYPE_CLASS_STRINGLIST) ? getEmptyStringList() :
getEmptyFSList();
}
/**
* Get an empty list from the type code of a list
* @param rangeCode -
* @return -
*/
public EmptyList getEmptyListFromTypeCode(int rangeCode) {
switch (rangeCode) {
case fsListTypeCode:
case fsEListTypeCode:
case fsNeListTypeCode: return getEmptyFSList();
case floatListTypeCode:
case floatEListTypeCode:
case floatNeListTypeCode: return getEmptyFloatList();
case intListTypeCode:
case intEListTypeCode:
case intNeListTypeCode: return getEmptyIntegerList();
case stringListTypeCode:
case stringEListTypeCode:
case stringNeListTypeCode: return getEmptyStringList();
default: throw new IllegalArgumentException();
}
}
// /**
// * Copies a feature, from one fs to another
// * FSs may belong to different CASes, but must have the same type system
// * Features must have compatible ranges
// * The target must not be indexed
// * The target must be a "new" (above the "mark") FS
// * @param fsSrc source FS
// * @param fi Feature to copy
// * @param fsTgt target FS
// */
// public static void copyFeature(TOP fsSrc, FeatureImpl fi, TOP fsTgt) {
// if (!copyFeatureExceptFsRef(fsSrc, fi, fsTgt, fi)) {
// if (!fi.isAnnotBaseSofaRef) {
// fsTgt._setFeatureValueNcNj(fi, fsSrc._getFeatureValueNc(fi));
// }
// }
// }
/**
* Copies a feature from one fs to another
* FSs may be in different type systems
* Doesn't copy a feature ref, but instead returns false.
* This is because feature refs can't cross CASes
* @param fsSrc source FS
* @param fiSrc feature in source to copy
* @param fsTgt target FS
* @param fiTgt feature in target to set
* @return false if feature is an fsRef
*/
public static boolean copyFeatureExceptFsRef(TOP fsSrc, FeatureImpl fiSrc, TOP fsTgt, FeatureImpl fiTgt) {
switch (fiSrc.getRangeImpl().getCode()) {
case booleanTypeCode : fsTgt._setBooleanValueNcNj( fiTgt, fsSrc._getBooleanValueNc( fiSrc)); break;
case byteTypeCode : fsTgt._setByteValueNcNj( fiTgt, fsSrc._getByteValueNc( fiSrc)); break;
case shortTypeCode : fsTgt._setShortValueNcNj( fiTgt, fsSrc._getShortValueNc( fiSrc)); break;
case intTypeCode : fsTgt._setIntValueNcNj( fiTgt, fsSrc._getIntValueNc( fiSrc)); break;
case longTypeCode : fsTgt._setLongValueNcNj( fiTgt, fsSrc._getLongValueNc( fiSrc)); break;
case floatTypeCode : fsTgt._setFloatValueNcNj( fiTgt, fsSrc._getFloatValueNc( fiSrc)); break;
case doubleTypeCode : fsTgt._setDoubleValueNcNj( fiTgt, fsSrc._getDoubleValueNc( fiSrc)); break;
case stringTypeCode : fsTgt._setStringValueNcNj( fiTgt, fsSrc._getStringValueNc( fiSrc)); break;
// case javaObjectTypeCode : fsTgt._setJavaObjectValueNcNj(fiTgt, fsSrc.getJavaObjectValue(fiSrc)); break;
// skip setting sofaRef - it's final and can't be set
default:
if (fiSrc.getRangeImpl().isStringSubtype()) {
fsTgt._setStringValueNcNj( fiTgt, fsSrc._getStringValueNc( fiSrc)); break; // does substring range check
}
return false;
} // end of switch
return true;
}
public static CommonArrayFS copyArray(TOP srcArray) {
CommonArrayFS srcCA = (CommonArrayFS) srcArray;
CommonArrayFS copy = (CommonArrayFS) srcArray._casView.createArray(srcArray._getTypeImpl(), srcCA.size());
copy.copyValuesFrom(srcCA);
return copy;
}
public BinaryCasSerDes getBinaryCasSerDes() {
return svd.bcsd;
}
/**
* @return the saved CommonSerDesSequential info
*/
CommonSerDesSequential getCsds() {
return svd.csds;
}
void setCsds(CommonSerDesSequential csds) {
svd.csds = csds;
}
CommonSerDesSequential newCsds() {
return svd.csds = new CommonSerDesSequential(this.getBaseCAS());
}
/**
* A space-freeing optimization for use cases where (multiple) delta CASes are being deserialized into this CAS and merged.
*/
public void deltaMergesComplete() {
svd.csds = null;
}
/******************************************
* PEAR support
* Don't modify the type system because it is in use on multiple threads
*
* Handling of id2fs for low level APIs:
* FSs in id2fs map are the outer non-pear ones
* Any gets do pear conversion if needed.
*
******************************************/
/**
* Convert base FS to Pear equivalent
* 3 cases:
* 1) no trampoline needed, no conversion, return the original fs
* 2) trampoline already exists - return that one
* 3) create new trampoline
* @param aFs
* @return
*/
static <T extends FeatureStructure> T pearConvert(T aFs) {
if (null == aFs) {
return null;
}
final TOP fs = (TOP) aFs;
final CASImpl view = fs._casView;
final TypeImpl ti = fs._getTypeImpl();
final FsGenerator3 generator = view.svd.generators[ti.getCode()];
if (null == generator) {
return aFs;
}
return (T) view.pearConvert(fs, generator);
}
/**
* Inner method - after determining there is a generator
* First see if already have generated the pear version, and if so,
* use that.
* Otherwise, create the pear version and save in trampoline table
* @param fs
* @param g
* @return
*/
private TOP pearConvert(TOP fs, FsGenerator3 g) {
return svd.id2tramp.putIfAbsent(fs._id, k -> {
svd.reuseId = k; // create new FS using base FS's ID
pearBaseFs = fs;
TOP r;
// createFS below is modified because of pearBaseFs non-null to
// "share" the int and data arrays
try {
r = g.createFS(fs._getTypeImpl(), this);
} finally {
svd.reuseId = 0;
pearBaseFs = null;
}
assert r != null;
if (r instanceof UimaSerializable) {
throw new UnsupportedOperationException(
"Pears with Alternate implementations of JCas classes implementing UimaSerializable not supported.");
// ((UimaSerializable) fs)._save_to_cas_data(); // updates in r too
// ((UimaSerializable) r)._init_from_cas_data();
}
return r;
});
}
/**
* Given a trampoline FS, return the corresponding base Fs
* Supports adding Fs (which must be a non-trampoline version) to indexes
* @param fs trampoline fs
* @return the corresponding base fs
*/
<T extends TOP> T getBaseFsFromTrampoline(T fs) {
TOP r = svd.id2base.get(fs._id);
assert r != null;
return (T) r;
}
/* *****************************************
* DEBUGGING and TRACING
******************************************/
public void traceFSCreate(FeatureStructureImplC fs) {
StringBuilder b = svd.traceFScreationSb;
if (b.length() > 0) {
traceFSflush();
}
// normally commented-out for matching with v2
// // mark annotations created by subiterator
// if (fs._getTypeCode() == TypeSystemConstants.annotTypeCode) {
// StackTraceElement[] stktr = Thread.currentThread().getStackTrace();
// if (stktr.length > 7 && stktr[6].getClassName().equals("org.apache.uima.cas.impl.Subiterator")) {
// b.append('*');
// }
// }
svd.id2addr.add(svd.nextId2Addr);
svd.nextId2Addr += fs._getTypeImpl().getFsSpaceReq((TOP)fs);
traceFSfs(fs);
svd.traceFSisCreate = true;
if (fs._getTypeImpl().isArray()) {
b.append(" l:").append(((CommonArrayFS)fs).size());
}
}
void traceFSfs(FeatureStructureImplC fs) {
StringBuilder b = svd.traceFScreationSb;
svd.traceFSid = fs._id;
b.append("c:").append(String.format("%-3d", getCasId()));
String viewName = fs._casView.getViewName();
if (null == viewName) {
viewName = "base";
}
b.append(" v:").append(Misc.elide(viewName, 8));
b.append(" i:").append(String.format("%-5s", geti2addr(fs._id)));
b.append(" t:").append(Misc.elide(fs._getTypeImpl().getShortName(), 10));
}
void traceIndexMod(boolean isAdd, TOP fs, boolean isAddbackOrSkipBag) {
StringBuilder b = svd.traceCowSb;
b.setLength(0);
b.append(isAdd ? (isAddbackOrSkipBag ? "abk_idx " : "add_idx ")
: (isAddbackOrSkipBag ? "rmv_auto_idx " : "rmv_norm_idx "));
// b.append(fs.toString());
b.append(fs._getTypeImpl().getShortName()).append(":").append(fs._id);
if (fs instanceof Annotation) {
Annotation ann = (Annotation) fs;
b.append(" begin: ").append(ann.getBegin());
b.append(" end: ").append(ann.getEnd());
b.append(" txt: \"").append(Misc.elide(ann.getCoveredText(), 10)).append("\"");
}
traceOut.println(b);
}
void traceCowCopy(FsIndex_singletype<?> index) {
StringBuilder b = svd.traceCowSb;
b.setLength(0);
b.append("cow-copy:");
b.append(" i: ").append(index);
traceOut.println(b);
}
void traceCowCopyUse(FsIndex_singletype<?> index) {
StringBuilder b = svd.traceCowSb;
b.setLength(0);
b.append("cow-copy-used:");
b.append(" i: ").append(index);
traceOut.println(b);
}
void traceCowReinit(String kind, FsIndex_singletype<?> index) {
StringBuilder b = svd.traceCowSb;
b.setLength(0);
b.append("cow-redo: ");
b.append(kind);
b.append(" i: ").append(index);
b.append(" c: ");
b.append(Misc.getCaller());
traceOut.println(b);
}
/** only used for tracing, enables tracing 2 slots for long/double */
private FeatureImpl prevFi;
void traceFSfeat(FeatureStructureImplC fs, FeatureImpl fi, Object v) {
//debug
FeatureImpl originalFi = fi;
StringBuilder b = svd.traceFScreationSb;
assert (b.length() > 0);
if (fs._id != svd.traceFSid) {
traceFSfeatUpdate(fs);
}
if (fi == null) { // happens on 2nd setInt call from cas copier copyfeatures for Long / Double
switch(prevFi.getSlotKind()) {
case Slot_DoubleRef: v = fs._getDoubleValueNc(prevFi); break; // correct double and long
case Slot_LongRef: v = fs._getLongValueNc(prevFi); break; // correct double and long
default: Misc.internalError();
}
fi = prevFi;
prevFi = null;
} else {
prevFi = fi;
}
String fn = fi.getShortName();
// correct calls done by cas copier fast loop
if (fi.getSlotKind() == SlotKind.Slot_DoubleRef) {
if (v instanceof Integer) {
return; // wait till the next part is traced
} else if (v instanceof Long) {
v = CASImpl.long2double((long)v);
}
}
if (fi.getSlotKind() == SlotKind.Slot_LongRef && (v instanceof Integer)) {
return; // output done on the next int call
}
String fv = getTraceRepOfObj(fi, v);
// if (geti2addr(fs._id).equals("79") &&
// fn.equals("sofa")) {
// new Throwable().printStackTrace(traceOut);
// }
// // debug
// if (fn.equals("lemma") &&
// fv.startsWith("Lemma:") &&
// debug1cnt < 2) {
// debug1cnt ++;
// traceOut.println("setting lemma feat:");
// new Throwable().printStackTrace(traceOut);
// }
// debug
// if (fs._getTypeImpl().getShortName().equals("Passage") &&
// "score".equals(fn) &&
// debug2cnt < 5) {
// debug2cnt++;
// traceOut.println("setting score feat in Passage");
// new Throwable().printStackTrace(traceOut);
// }
// debug
int i_v = Math.max(0, 10 - fn.length());
int i_n = Math.max(0, 10 - fv.length());
fn = Misc.elide(fn, 10 + i_n, false);
fv = Misc.elide(fv, 10 + i_v, false);
// debug
// if (!svd.traceFSisCreate && fn.equals("uninf.dWord") && fv.equals("XsgTokens")) {
// traceOut.println("debug uninf.dWord:XsgTokens: " + Misc.getCallers(3, 10));
// }
b.append(' ').append(Misc.elide(fn + ':' + fv, 21));
// value of a feature:
// - "null" or
// - if FS: type:id (converted to addr)
// - v.toString()
}
private static int debug2cnt = 0;
/**
* @param v
* @return value of the feature:
* "null" or if FS: type:id (converted to addr) or v.toString()
* Note: white space in strings converted to "_' characters
*/
private String getTraceRepOfObj(FeatureImpl fi, Object v) {
if (v instanceof TOP) {
TOP fs = (TOP) v;
return Misc.elide(fs.getType().getShortName(), 5, false) + ':' + geti2addr(fs._id);
}
if (v == null) return "null";
if (v instanceof String) {
String s = Misc.elide((String) v, 50, false);
return Misc.replaceWhiteSpace(s, "_");
}
if (v instanceof Integer) {
int iv = (int) v;
switch (fi.getSlotKind()) {
case Slot_Boolean: return (iv == 1) ? "true" : "false";
case Slot_Byte:
case Slot_Short:
case Slot_Int: return Integer.toString(iv);
case Slot_Float: return Float.toString(int2float(iv));
}
}
if (v instanceof Long) {
long vl = (long) v;
return (fi.getSlotKind() == SlotKind.Slot_DoubleRef)
? Double.toString(long2double(vl))
: Long.toString(vl);
}
return Misc.replaceWhiteSpace(v.toString(), "_");
}
private String geti2addr(int id) {
if (id >= svd.id2addr.size()) {
return Integer.toString(id) + '!';
}
return Integer.toString(svd.id2addr.get(id));
}
void traceFSfeatUpdate(FeatureStructureImplC fs) {
traceFSflush();
traceFSfs(fs);
svd.traceFSisCreate = false;
}
public StringBuilder traceFSflush() {
if (!traceFSs) return null;
StringBuilder b = svd.traceFScreationSb;
if (b.length() > 0) {
traceOut.println((svd.traceFSisCreate ? "cr: " :
"up: ") + b);
b.setLength(0);
svd.traceFSisCreate = false;
}
return b;
}
private static class MeasureSwitchType {
TypeImpl oldType;
TypeImpl newType;
String oldJCasClassName;
String newJCasClassName;
int count = 0;
boolean newSubsumesOld;
boolean oldSubsumesNew;
long scantime = 0;
MeasureSwitchType(TypeImpl oldType, TypeImpl newType) {
this.oldType = oldType;
this.oldJCasClassName = oldType.getJavaClass().getName();
this.newType = newType;
this.newJCasClassName = newType.getJavaClass().getName();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((newJCasClassName == null) ? 0 : newJCasClassName.hashCode());
result = prime * result + ((newType == null) ? 0 : newType.hashCode());
result = prime * result + ((oldJCasClassName == null) ? 0 : oldJCasClassName.hashCode());
result = prime * result + ((oldType == null) ? 0 : oldType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MeasureSwitchType)) {
return false;
}
MeasureSwitchType other = (MeasureSwitchType) obj;
if (newJCasClassName == null) {
if (other.newJCasClassName != null) {
return false;
}
} else if (!newJCasClassName.equals(other.newJCasClassName)) {
return false;
}
if (newType == null) {
if (other.newType != null) {
return false;
}
} else if (!newType.equals(other.newType)) {
return false;
}
if (oldJCasClassName == null) {
if (other.oldJCasClassName != null) {
return false;
}
} else if (!oldJCasClassName.equals(other.oldJCasClassName)) {
return false;
}
if (oldType == null) {
if (other.oldType != null) {
return false;
}
} else if (!oldType.equals(other.oldType)) {
return false;
}
return true;
}
}
private static final Map<MeasureSwitchType, MeasureSwitchType> measureSwitches = new HashMap<>();
static {
if (MEASURE_SETINT) {
Runtime.getRuntime().addShutdownHook(new Thread(null, () -> {
System.out.println("debug Switch Types dump, # entries: " + measureSwitches.size());
int s1 = 0, s2 = 0, s3 = 0;
for (MeasureSwitchType mst : measureSwitches.keySet()) {
s1 = Math.max(s1, mst.oldType.getName().length());
s2 = Math.max(s2, mst.newType.getName().length());
s3 = Math.max(s3, mst.oldJCasClassName.length());
}
for (MeasureSwitchType mst : measureSwitches.keySet()) {
System.out.format("count: %,6d scantime = %,7d ms, subsumes: %s %s, type: %-" + s1 + "s newType: %-" + s2 + "s, cl: %-" + s3 + "s, newCl: %s%n",
mst.count, mst.scantime / 1000000,
mst.newSubsumesOld ? "n>o" : " ",
mst.oldSubsumesNew ? "o>w" : " ",
mst.oldType.getName(), mst.newType.getName(), mst.oldJCasClassName, mst.newJCasClassName);
}
// if (traceFSs) {
// System.err.println("debug closing traceFSs output");
// traceOut.close();
// }
}, "Dump SwitchTypes"));
}
// this is definitely needed
if (traceFSs) {
Runtime.getRuntime().addShutdownHook(new Thread(null, () -> {
System.out.println("closing traceOut");
traceOut.close();
}, "close trace output"));
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.cas.admin.CASMgr#setCAS(org.apache.uima.cas.CAS)
* Internal use Never called Kept because it's in the interface.
*/
@Override
@Deprecated
public void setCAS(CAS cas) {}
/**
* @return true if in Pear context, or external context outside AnalysisEngine having a UIMA Extension class loader
* e.g., if calling a call-back routine loaded outside the AE.
*/
boolean inPearContext() {
return svd.previousJCasClassLoader != null;
}
/**
* Pear context suspended while creating a base version, when we need to create a new FS
* (we need to create both the base and the trampoline version)
*/
private void suspendPearContext() {
svd.suspendPreviousJCasClassLoader = svd.previousJCasClassLoader;
svd.previousJCasClassLoader = null;
}
private void restorePearContext() {
svd.previousJCasClassLoader = svd.suspendPreviousJCasClassLoader;
}
/**
*
* @return the initial heap size specified or defaulted
*/
public int getInitialHeapSize() {
return this.svd.initialHeapSize;
}
// backwards compatibility - reinit calls
// just the public apis
/**
* Deserializer for Java-object serialized instance of CASSerializer
* Used by Soap
* @param ser - The instance to convert back to a CAS
*/
public void reinit(CASSerializer ser) {
svd.bcsd.reinit(ser);
}
/**
* Deserializer for CASCompleteSerializer instances - includes type system and index definitions
* Never delta
* @param casCompSer -
*/
public void reinit(CASCompleteSerializer casCompSer) {
svd.bcsd.reinit(casCompSer);
}
/**
* ---------------------------------------------------------------------
* see Blob Format in CASSerializer
*
* This reads in and deserializes CAS data from a stream. Byte swapping may be
* needed if the blob is from C++ -- C++ blob serialization writes data in
* native byte order.
*
* Supports delta deserialization. For that, the the csds from the serialization event must be used.
*
* @param istream -
* @return - the format of the input stream detected
* @throws CASRuntimeException wraps IOException
*/
public SerialFormat reinit(InputStream istream) throws CASRuntimeException {
return svd.bcsd.reinit(istream);
}
void maybeHoldOntoFS(FeatureStructureImplC fs) {
if (IS_ALWAYS_HOLD_ONTO_FSS) {
svd.id2fs.putUnconditionally((TOP)fs);
}
}
// int allocIntData(int sz) {
//
// if (sz > INT_DATA_FOR_ALLOC_SIZE / 4) {
// returnIntDataForAlloc = new int[sz];
// return 0;
// }
//
// if (sz + nextIntDataOffsetForAlloc > INT_DATA_FOR_ALLOC_SIZE) {
// // too large to fit, alloc a new one
// currentIntDataForAlloc = new int[INT_DATA_FOR_ALLOC_SIZE];
// nextIntDataOffsetForAlloc = 0;
// }
// int r = nextIntDataOffsetForAlloc;
// nextIntDataOffsetForAlloc += sz;
// returnIntDataForAlloc = currentIntDataForAlloc;
// return r;
// }
//
// int[] getReturnIntDataForAlloc() {
// return returnIntDataForAlloc;
// }
//
// int allocRefData(int sz) {
//
// if (sz > REF_DATA_FOR_ALLOC_SIZE / 4) {
// returnRefDataForAlloc = new Object[sz];
// return 0;
// }
//
// if (sz + nextRefDataOffsetForAlloc > REF_DATA_FOR_ALLOC_SIZE) {
// // too large to fit, alloc a new one
// currentRefDataForAlloc = new Object[REF_DATA_FOR_ALLOC_SIZE];
// nextRefDataOffsetForAlloc = 0;
// }
// int r = nextRefDataOffsetForAlloc;
// nextRefDataOffsetForAlloc += sz;
// returnRefDataForAlloc = currentRefDataForAlloc;
// return r;
// }
//
// Object[] getReturnRefDataForAlloc() {
// return returnRefDataForAlloc;
// }
}