| /* |
| * 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.jcas.impl; |
| |
| // * todo: |
| // * |
| // * Compatibility removes at some point: TypeSystemInit and it's caller |
| |
| import java.io.InputStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| import org.apache.uima.UIMAFramework; |
| import org.apache.uima.cas.AbstractCas; |
| import org.apache.uima.cas.AbstractCas_ImplBase; |
| 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.ConstraintFactory; |
| 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.SofaFS; |
| import org.apache.uima.cas.SofaID; |
| import org.apache.uima.cas.Type; |
| import org.apache.uima.cas.TypeSystem; |
| import org.apache.uima.cas.impl.CASImpl; |
| import org.apache.uima.cas.impl.FSClassRegistry; |
| import org.apache.uima.cas.impl.FSGenerator; |
| import org.apache.uima.cas.impl.FeatureStructureImpl; |
| import org.apache.uima.cas.impl.LowLevelCAS; |
| import org.apache.uima.cas.impl.LowLevelException; |
| import org.apache.uima.cas.impl.LowLevelIndexRepository; |
| import org.apache.uima.cas.impl.TypeImpl; |
| import org.apache.uima.cas.impl.TypeSystemImpl; |
| import org.apache.uima.cas.text.AnnotationIndex; |
| import org.apache.uima.internal.util.UIMAClassLoader; |
| import org.apache.uima.jcas.JCas; |
| import org.apache.uima.jcas.JCasRegistry; |
| import org.apache.uima.jcas.JFSIndexRepository; |
| import org.apache.uima.jcas.cas.AnnotationBase; |
| 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.Sofa; |
| import org.apache.uima.jcas.cas.StringArray; |
| import org.apache.uima.jcas.cas.TOP; |
| import org.apache.uima.jcas.cas.TOP_Type; |
| import org.apache.uima.jcas.tcas.Annotation; |
| import org.apache.uima.util.Level; |
| import org.apache.uima.util.Logger; |
| |
| // ********************************* |
| // * Implementation of JCas * |
| // ********************************* |
| |
| /* |
| * |
| * Overview -------- |
| * |
| * This design uses classes for types, not Interfaces. JCas CAS types are represented in a running |
| * server by a collection of classes A set of 2 classes for each loaded equivalent-to-CAS type Foo |
| * (Class) Foo_Type (Class) |
| * |
| * JCas and Foo_Type style classes have one instantiation per CAS View |
| * |
| * Integrated with Framework Design -------------------------------- Assume: Framework collects for |
| * each CAS creation all the types that will be defined for this CAS. Note: can create multiple |
| * instances of CASs (and JCases) each TAE/App. |
| * |
| * Assume: Each JCas instance (associated with a CAS) is single-threaded. if not - must synchronize |
| * refs to tables/arrays associated with this |
| * |
| * Assume: Each CAS instance only has one associated set of types, specified by a String[] of |
| * fully-qual-type-names Implication: if CAS type system changes, create new JCas instance. This |
| * allows many things to be "final". |
| * |
| * Integrate with UIMA Framework Resource Manager for use of class loaders. |
| * |
| * Framework will call getJCas once after the CAS types are completely specified, or when a |
| * deserialization has re-initialized a CAS. |
| * |
| * Initialize: To get to the type object from Foo, use jcas.getType(Foo.type) To get to the JCas |
| * type object from instance of Foo, use this.jcasType. |
| * |
| * If the CAS has its contents reset, call jcas.clearData() to reset the corresponding JCas content. |
| * |
| * Implementation Notes: At loadtime, Foo and Foo_Type classes are assigned static integer indexes, |
| * using the JCasRegistry. These indexes are used in arrays in the jcas instance to go from Foo |
| * class (not instances) to Foo_Type instances (one per CAS). Things which require "types" at |
| * runtime reference the Foo_Type instances. |
| * |
| * Maps: Note: per CAS means per shared JCas instance assoc w/ CAS |
| * |
| * (universal) (static field in class) go from Foo class to index unique ID used with next map to |
| * locate Foo_Type instance associated with this class If universal - becomes an index for all |
| * FooStyle classes loaded (per CAS) |
| * |
| * (ArrayList) map index from Foo (static) to Foo_Type instance used in creating new instances. |
| * Needs to be one per CAS because a particular "xyz" feature in CAS1 != "xyz" feature in CAS2 If |
| * above universal, then can become large array with mostly unused slots. Possibility: reuse |
| * no-longer-used slots. Identify no-longer-used slots at CAS Type unload event? |
| */ |
| |
| /** |
| * implements the supporting infrastructure for JCas model linked with a Cas. There is one logical |
| * instance of this instantiated per Cas or CasView. If you hold a reference to a CAS, to get a |
| * reference to the corresponding JCas, use the method getJCas(). Likewise, if you hold a reference |
| * to this object, you can get a reference to the corresponding CAS object using the method |
| * getCas(). |
| * <p> |
| * There are variables here that hold references to constant, frequently needed Cas values, such as |
| * 0-length FSArrays, 0-length Strings, etc. These can be used to improve efficiency, since the same |
| * object can be shared and used for all instances because it is unchangeable. These objects are |
| * reset when the CAS is reset, and are initialized lazily, on first use. |
| */ |
| |
| public class JCasImpl extends AbstractCas_ImplBase implements AbstractCas, JCas { |
| // * FSIndexRepository - the get method returns the java type * |
| // * so FSIndexRepository can't be in the implements clause * |
| |
| // constant used in the following function |
| /** internal use - constant used in getting constructors */ |
| final static private Class<?>[] jcasBaseAndType = new Class[] { JCas.class, Type.class }; |
| |
| final static private Class<?>[] intAnd_Type = new Class[] { int.class, TOP_Type.class }; |
| |
| |
| private static class LoadedJCasType<T extends TOP> { |
| final String typeName; |
| |
| final int index; |
| |
| final boolean isSubtypeOfAnnotationBase; |
| |
| final Constructor<? extends TOP_Type> constructorFor_Type; |
| |
| final Constructor<T> constructorForType; |
| |
| LoadedJCasType(String typeName, Class<? extends TOP_Type> a_TypeClass, ClassLoader cl) { |
| Class<? extends TOP_Type> _TypeClass = a_TypeClass; |
| this.typeName = typeName; |
| int tempindex = 0; |
| boolean tempisSubtypeOfAnnotationBase = false; |
| Constructor<? extends TOP_Type> tempconstructorFor_Type = null; |
| Constructor<T> tempconstructorForType = null; |
| |
| String name = _TypeClass.getName(); |
| try { |
| @SuppressWarnings("unchecked") |
| Class<T> typeClass = (Class<T>) Class.forName(name.substring(0, name.length() - 5), true, cl); // drop |
| // _Type |
| |
| Field typeIndexField = null; |
| try { |
| typeIndexField = _TypeClass.getDeclaredField("typeIndexID"); |
| } catch (NoSuchFieldException e) { |
| try { |
| // old version has the index in the base type |
| typeIndexField = typeClass.getDeclaredField("typeIndexID"); |
| } catch (NoSuchFieldException e2) { |
| logAndThrow(e2); |
| } |
| } |
| tempindex = typeIndexField.getInt(null); // null - static instance var |
| tempconstructorFor_Type = _TypeClass.getDeclaredConstructor(jcasBaseAndType); |
| tempconstructorForType = typeClass.getDeclaredConstructor(intAnd_Type); |
| tempisSubtypeOfAnnotationBase = AnnotationBase.class.isAssignableFrom(typeClass); |
| } catch (SecurityException e) { |
| logAndThrow(e); |
| } catch (NoSuchMethodException e) { |
| logAndThrow(e); |
| } catch (ClassNotFoundException e) { |
| logAndThrow(e); |
| } catch (IllegalArgumentException e) { |
| logAndThrow(e); |
| } catch (IllegalAccessException e) { |
| logAndThrow(e); |
| } finally { |
| index = tempindex; |
| constructorFor_Type = tempconstructorFor_Type; |
| constructorForType = tempconstructorForType; |
| isSubtypeOfAnnotationBase = tempisSubtypeOfAnnotationBase; |
| } |
| } |
| |
| private void logAndThrow(Exception e) { |
| CASRuntimeException casEx = new CASRuntimeException(CASRuntimeException.JCAS_CAS_MISMATCH); |
| casEx.initCause(e); |
| throw casEx; |
| } |
| } |
| |
| static public class ErrorReport { |
| final Exception e; |
| final boolean doThrow; |
| ErrorReport(Exception e, boolean doThrow) { |
| this.e = e; |
| this.doThrow = doThrow; |
| } |
| } |
| |
| // ************************************************* |
| // * Static Data shared with all instances of JCas * |
| // ************************************************* |
| private final static int INITIAL_HASHMAP_SIZE = 256; |
| |
| // key = typeSystemImpl instance, value = Maps: |
| // key = class loader instance, value = Maps: |
| // key = string = fully qualified java type names of _Type classes, |
| // value = instances of LoadedJCasType: |
| // fully-qualified-name-of-CAS-type |
| // the _Type class for this (specific to class loader) |
| // the class loader instance |
| |
| |
| // Note: cannot use treemap here because keys (class loaders) are not comparable (have no sort |
| // order) |
| // To use: first look up with key = typeSystemImpl instance (uses object identity ("==") test) |
| // Then use value and look up using class loader (uses object identity ("==") test) |
| |
| // The first two maps (with keys typeSystemImpl and classLoader) are |
| // Weak maps so that they don't hold onto their keys (class loader or type system) if |
| // these are no longer in use |
| // |
| // This is a static map - effectively indexed by all loaded type systems and all classloaders |
| |
| // Access to this must be synch'd |
| private static Map<TypeSystemImpl, Map<ClassLoader, Map<String, LoadedJCasType<?>>>> typeSystemToLoadedJCasTypesByClassLoader = |
| new WeakHashMap<TypeSystemImpl, Map<ClassLoader, Map<String, LoadedJCasType<?>>>>(4); |
| |
| // ********************************************** |
| // * Data shared among views of a single CAS * |
| // * We keep one copy per view set * |
| // **********************************************/ |
| private static class JCasSharedView { |
| // ******************************************************** |
| // * Data shared among all views in a (J)CAS View group * |
| // * Access to this data is assumed to be single threaded * |
| // ******************************************************** |
| |
| /** |
| * key = CAS addr, value = corresponding Java instance. |
| * |
| * The Java instance may be a JCas cover object, or a non-JCas Java cover object of type FeatureStructureImplC |
| * |
| *This impl was changed in May 2007 to a |
| * design of one cover object per CAS object, when dealing with multiple views. This implements |
| * better semantics for co-located components sharing these objects - they all get to share the |
| * same object, independent of which view(s) the object may be indexed in. For cases where the |
| * user has augmented the definition of the JCas object to have native Java fields, this design |
| * is closer to the user's expectation. |
| * |
| * The only use for multiple objects, previously, was to implement the 0-argument version of |
| * addToIndexes and removeFromIndexes. The new implementation will come close (but not be |
| * perfectly the same) as the old implementation, by doing the following: |
| * |
| * Changing the "new" and get/next from iterator methods to set the _Type field of the retrieved |
| * cover class object (or new object) to the correct view. |
| * |
| * Deref objects follow many paths. They all have a default view they create the object with |
| * respect to. A simple deref uses the same cas ref the original object had. |
| * |
| */ |
| private JCasHashMap cAddr2Jfs; |
| |
| private final Map<ClassLoader, JCasHashMap> cAddr2JfsByClassLoader = new IdentityHashMap<ClassLoader, JCasHashMap>(); |
| |
| /* convenience holders of CAS constants that may be useful */ |
| /* initialization done lazily - on first call to getter */ |
| |
| public StringArray stringArray0L = null; |
| |
| public IntegerArray integerArray0L = null; |
| |
| public FloatArray floatArray0L = null; |
| |
| public FSArray fsArray0L = null; |
| |
| // * collection of errors that occur during initialization |
| public Collection<ErrorReport> errorSet = new ArrayList<ErrorReport>(); |
| |
| public ClassLoader currentClassLoader = null; |
| |
| private ClassLoader cacheClassLoaderInitialized; |
| final private Map<ClassLoader, Boolean> isInitializedForClassLoader = Collections.synchronizedMap(new IdentityHashMap<ClassLoader, Boolean>()); |
| |
| private JCasSharedView(CASImpl aCAS, boolean useJcasCache) { |
| setupJCasHashMap(aCAS.getJCasClassLoader(), useJcasCache, aCAS.getHeap().getInitialSize() / 16); |
| } |
| |
| private void setupJCasHashMap(ClassLoader cl, boolean isUsedCache, int initialSize) { |
| int size = Math.max(INITIAL_HASHMAP_SIZE, initialSize); |
| cAddr2Jfs = new JCasHashMap(size, isUsedCache); |
| cAddr2JfsByClassLoader.put(cl, cAddr2Jfs); |
| currentClassLoader = cl; |
| } |
| } |
| |
| // ******************* |
| // * Data per (J)CAS * |
| // * There may be multiples of these for one base CAS - one per "view" |
| // * Access to this data is assumed to be single threaded |
| // ******************* |
| |
| private final JCasSharedView sharedView; |
| |
| // not public to protect it from accidents |
| private final CASImpl casImpl; |
| |
| private final LowLevelIndexRepository ll_IndexRepository; |
| |
| private final JFSIndexRepository jfsIndexRepository; |
| |
| private final boolean isUsedCache; |
| |
| /* |
| * typeArray is one per CAS because it holds pointers to instances of _Type objects, per CAS. |
| * It is not in the shared view, because the typeArray points to instances that go with the view. |
| * This is used when making instances - it allows each instance to be associated with a view, |
| * for purposes of making addtoIndexes() and removeFromIndexes() work. |
| * It is not final, because it may need to be "extended" if |
| * alternate versions of types are loaded from different class loaders, at some point in the |
| * execution. The alternate versions are given their own slots in this array. |
| */ |
| private TOP_Type[] typeArray = new TOP_Type[0]; // contents are subtypes of TOP_Type |
| |
| // ********************************* |
| // * Getters for read-only objects * |
| // ********************************* |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getFSIndexRepository() |
| */ |
| public FSIndexRepository getFSIndexRepository() { |
| return casImpl.getIndexRepository(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getLowLevelIndexRepository() |
| */ |
| public LowLevelIndexRepository getLowLevelIndexRepository() { |
| return ll_IndexRepository; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getCas() |
| */ |
| public CAS getCas() { |
| return casImpl; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getCasImpl() |
| */ |
| public CASImpl getCasImpl() { |
| return casImpl; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getLowLevelCas() |
| */ |
| public LowLevelCAS getLowLevelCas() { |
| return casImpl; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getTypeSystem() |
| */ |
| public TypeSystem getTypeSystem() { |
| return casImpl.getTypeSystem(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getType(int) |
| */ |
| public TOP_Type getType(final int i) { |
| if (i >= typeArray.length || null == typeArray[i]) { |
| getTypeInit(i); |
| } |
| return typeArray[i]; |
| } |
| |
| /** |
| * Map from type codes to _Type instances kept per view in the field typeArray |
| * @param i |
| */ |
| private void getTypeInit(final int i) { |
| // unknown ID. This may be due to a need to update the typeArray |
| // due to switching class loaders. This updating is done |
| // partially lazily - when needed, beyond the particular CAS instance and |
| // view that was being passed to a process method when the |
| // class loader switch was done. |
| |
| // In order for this to work, all access to the typeArray must be |
| // via this getter. |
| |
| // Try instantiating the entries in typeArray for this class loader |
| instantiateJCas_Types(this.sharedView.currentClassLoader); |
| if (i >= typeArray.length || null == typeArray[i]) { |
| |
| // unknown ID. Attempt to get offending class. |
| Class<? extends TOP> cls = JCasRegistry.getClassForIndex(i); |
| if (cls != null) { |
| String typeName = cls.getName(); |
| // is type in type system |
| if (this.casImpl.getTypeSystem().getType(typeName) == null) { |
| // no - report error that JCAS type was not defined in XML |
| // descriptor |
| CASRuntimeException casEx = new CASRuntimeException( |
| CASRuntimeException.JCAS_TYPE_NOT_IN_CAS, new String[] { typeName }); |
| throw casEx; |
| } else { |
| // yes - there was some problem loading the _Type object |
| CASRuntimeException casEx = new CASRuntimeException( |
| CASRuntimeException.JCAS_MISSING_COVERCLASS, new String[] { typeName + "_Type" }); |
| throw casEx; |
| } |
| |
| } else { |
| throw new CASRuntimeException(CASRuntimeException.JCAS_UNKNOWN_TYPE_NOT_IN_CAS); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getCasType(int) |
| */ |
| public Type getCasType(int i) { |
| return getType(i).casType; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getType(org.apache.uima.jcas.cas.TOP) |
| */ |
| public TOP_Type getType(TOP instance) { |
| return getType(instance.getTypeIndexID()); |
| } |
| |
| /** throws (an unchecked) CASRuntimeException */ |
| private void logAndThrow(Exception e) { |
| CASRuntimeException casEx = new CASRuntimeException(CASRuntimeException.JCAS_CAS_MISMATCH); |
| casEx.initCause(e); |
| throw casEx; |
| } |
| |
| // never called, but have to set values to null because they're final |
| private JCasImpl() { |
| sharedView = null; |
| casImpl = null; |
| ll_IndexRepository = null; |
| isUsedCache = false; |
| throw new RuntimeException("JCas constructor with no args called, should never be called."); |
| } |
| |
| /* |
| * Private constructor, called when new instance (either for new cas, or for old cas but new |
| * Annotator/Application class, needed |
| * |
| * Called by JCas.getJCas(cas) |
| * |
| * The CAS must be initialized when this is called. |
| * |
| */ |
| private JCasImpl(CASImpl cas) throws CASException { |
| |
| // * A new instance of JCas exists for each CAS |
| // * At this point, some but not necessarily all of the Types have been |
| // loaded |
| |
| // * the typeArray needs to be big enough to hold all the types |
| // * that will be loaded. |
| |
| this.casImpl = cas; |
| this.isUsedCache = cas.doUseJcasCache(); |
| if (casImpl != casImpl.getBaseCAS()) { |
| sharedView = ((JCasImpl) casImpl.getBaseCAS().getJCas()).sharedView; |
| sharedView.errorSet.clear(); |
| } else { |
| sharedView = new JCasSharedView(cas, this.isUsedCache); |
| } |
| |
| this.ll_IndexRepository = casImpl.ll_getIndexRepository(); |
| this.jfsIndexRepository = new JFSIndexRepositoryImpl(this, cas.getIndexRepository()); |
| |
| // * acquire the lock for this thread that is the same lock |
| // * used in the getNextIndexIncr operation. This will block |
| // * any other (meaning on another thread) |
| // * loading of the JCas Type classes until this thread's loading |
| // * completes. |
| |
| synchronized (JCasImpl.class) { |
| ClassLoader cl = cas.getJCasClassLoader(); |
| instantiateJCas_Types(cl); |
| } // end of synchronized block |
| } |
| |
| /** |
| * This is only called when the JCas is already set up |
| * |
| * @param cl |
| * class loader to switch to |
| */ |
| public void switchClassLoader(ClassLoader cl) { |
| |
| // first try a fast switch - works if we've switched before to this class loader |
| if (!casImpl.getFSClassRegistry().swapInGeneratorsForClassLoader(cl, casImpl)) { |
| instantiateJCas_Types(cl); |
| } |
| final JCasSharedView sv = this.sharedView; |
| sv.cAddr2Jfs = sv.cAddr2JfsByClassLoader.get(cl); |
| if (null == sv.cAddr2Jfs) { |
| // initial size low because this is the use case where a Pear isolation is happening, |
| // it is more likely that the PEAR is only doing a subset of the JCas things |
| sv.setupJCasHashMap(cl, isUsedCache, casImpl.getHeap().getInitialSize() / 1024); |
| } else { |
| sv.currentClassLoader = cl; |
| } |
| } |
| |
| /** |
| * There may be several type systems with different defined types loaded and operating at the same |
| * time (in different CASes) There is an instance of the loaded JCas Classes for each Type System |
| * for each class loader. |
| * |
| * @param cl |
| * @return a Map from the string of the fully qualified type name to LoadedJCasType instances |
| */ |
| @SuppressWarnings("unchecked") |
| private synchronized Map<String, LoadedJCasType<?>> loadJCasClasses(ClassLoader cl) { |
| final TypeSystem ts = casImpl.getTypeSystem(); |
| Iterator<Type> typeIt = ts.getTypeIterator(); |
| TypeImpl t; |
| String casName; |
| Map<String, LoadedJCasType<?>> jcasTypes = new HashMap<>(); |
| |
| // * note that many of these may have already been loaded |
| // * load all the others. Actually, we ask to load all the types |
| // * and the ones already loaded - we just get their loaded versions |
| // returned. |
| // * Loading will run the static init functions. |
| |
| while (typeIt.hasNext()) { |
| t = (TypeImpl) typeIt.next(); |
| casName = t.getName(); |
| String name_Type; |
| if (builtInsWithNoJCas.contains(casName)) |
| continue; |
| if (builtInsWithAltNames.contains(casName)) |
| // * convert uima.cas.Xxx -> org.apache.uima.jcas.cas.Xxx |
| // * convert uima.tcas.Annotator -> org.apache.uima.jcas.tcas.Annotator |
| try { |
| String nameBase = "org.apache.uima.jcas." + casName.substring(5); |
| name_Type = nameBase + "_Type"; |
| jcasTypes.put(nameBase, new LoadedJCasType<>(t.getName(), (Class<? extends TOP_Type>) Class |
| .forName(name_Type, true, cl), cl)); |
| } catch (ClassNotFoundException e1) { |
| // OK for DocumentAnnotation, which may not have a cover class. |
| // Otherwise, not OK. |
| if (!CAS.TYPE_NAME_DOCUMENT_ANNOTATION.equals(casName)) { |
| assert false : "never get here because built-ins have java cover types"; |
| e1.printStackTrace(); |
| } |
| } |
| |
| // this is done unconditionally to pick up old style cover functions if |
| // any |
| // as well as other JCas model types |
| try { |
| name_Type = casName + "_Type"; |
| jcasTypes.put(casName, new LoadedJCasType<>(t.getName(), (Class<? extends TOP_Type>) Class.forName(name_Type, true, cl), |
| cl)); |
| // also force the load the plain name without _Type for |
| // old-style - that's where |
| // the index is incremented |
| Class.forName(casName, true, cl); |
| } catch (ClassNotFoundException e1) { |
| // many classes may not have JCas models, so this is not an |
| // error |
| } |
| } |
| |
| // note: this entire method is synchronized |
| Map<ClassLoader, Map<String, LoadedJCasType<?>>> classLoaderToLoadedJCasTypes = typeSystemToLoadedJCasTypesByClassLoader.get(casImpl |
| .getTypeSystemImpl()); |
| if (null == classLoaderToLoadedJCasTypes) { |
| classLoaderToLoadedJCasTypes = new WeakHashMap<ClassLoader, Map<String, LoadedJCasType<?>>>(4); |
| typeSystemToLoadedJCasTypesByClassLoader.put(casImpl.getTypeSystemImpl(), |
| classLoaderToLoadedJCasTypes); |
| } |
| classLoaderToLoadedJCasTypes.put(cl, jcasTypes); |
| |
| expandTypeArrayIfNeeded(); |
| return jcasTypes; |
| } |
| |
| // note: callers are not synchronized because the typeArray has a |
| // separate instance per CAS (actually per CAS view) |
| private void expandTypeArrayIfNeeded() { |
| if (typeArray.length < JCasRegistry.getNumberOfRegisteredClasses()) { |
| TOP_Type[] newTypeArray = new TOP_Type[JCasRegistry.getNumberOfRegisteredClasses()]; |
| System.arraycopy(typeArray, 0, newTypeArray, 0, typeArray.length); |
| typeArray = newTypeArray; |
| } |
| } |
| |
| /** |
| * called when switching to JCas and the JCas already exists to check if |
| * the JCas needs to have classes loaded for this class loader. This can happen when the JCas is first |
| * instantiated while under the scope of a nested UIMA class loader. This could be a Pear class loader, or |
| * even just an ordinary UIMA Class loader for a pipeline, where the Framework is running an "exit routine" supplied |
| * by the user, but loaded in a (for example) initial application loader context. |
| * @param cl the class loader in use |
| */ |
| public void maybeInitializeForClassLoader(ClassLoader cl) { |
| if (sharedView.cacheClassLoaderInitialized == cl) { |
| return; |
| } |
| if (this.sharedView.isInitializedForClassLoader.get(cl) == null) { |
| instantiateJCas_Types(cl); |
| } |
| sharedView.cacheClassLoaderInitialized = cl; |
| } |
| |
| /** |
| * |
| * @param cl the class loader to use as the initiating loader for loading JCas classes |
| */ |
| public void instantiateJCas_Types(ClassLoader cl) { |
| Map<String, LoadedJCasType<?>> loadedJCasTypes = null; |
| FSClassRegistry fscr = casImpl.getFSClassRegistry(); |
| boolean alreadyLoaded; // means the "classes" have been loaded, but doesn't mean |
| // the _Type instances of those classes have been created. |
| boolean anyNewInstances = false; // true if any new instances of _Type are generated |
| FSGenerator<?>[] newFSGeneratorSet; |
| synchronized (JCasImpl.class) { |
| Map<ClassLoader, Map<String, LoadedJCasType<?>>> classLoaderToLoadedJCasTypes = typeSystemToLoadedJCasTypesByClassLoader.get(casImpl |
| .getTypeSystemImpl()); |
| if (null != classLoaderToLoadedJCasTypes) { |
| loadedJCasTypes = classLoaderToLoadedJCasTypes.get(cl); |
| } |
| alreadyLoaded = (null != loadedJCasTypes); |
| if (!alreadyLoaded) { |
| loadedJCasTypes = loadJCasClasses(cl); |
| } |
| |
| expandTypeArrayIfNeeded(); |
| // if already loaded, can skip making new generators - |
| // in this case newFSGeneratorSet is never referenced |
| // Set it to null for "safety" |
| // If not already loaded, initialize the generators to a clone of the FSClassRegistry generators. |
| newFSGeneratorSet = (alreadyLoaded ? null : fscr.getNewFSGeneratorSet()); |
| for (Iterator<Map.Entry<String, LoadedJCasType<?>>> it = loadedJCasTypes.entrySet().iterator(); it.hasNext();) { |
| |
| // Explanation for this logic: |
| // Instances of _Types are kept per class loader, per Cas (e.g., in the cas pool) |
| // and per view (to support having the ref to the casImpl be to the right view |
| // so add-to-indexes works better). |
| // The "typeArray" field is per view. |
| // When switching class loaders (e.g. a pear in the pipeline), some of the |
| // _Type instances (e.g. the built-ins) might be already instantiated, but others are not |
| // |
| boolean madeNewInstance = makeInstanceOf_Type(it.next().getValue(), alreadyLoaded, |
| newFSGeneratorSet); |
| anyNewInstances = madeNewInstance || anyNewInstances; |
| } |
| |
| // speed up - skip rest if nothing to do |
| if (!anyNewInstances && |
| sharedView.isInitializedForClassLoader.get(cl) != null) { // don't skip if need to install the new generators for this class loader UIMA-5055 |
| return; |
| } |
| if (!alreadyLoaded) { |
| copyDownSuperGenerators(loadedJCasTypes, newFSGeneratorSet); |
| if (casImpl.usingBaseClassLoader()) { |
| fscr.setBaseGenerators(newFSGeneratorSet); // should be under sync lock |
| } |
| fscr.saveGeneratorsForClassLoader(cl, newFSGeneratorSet); |
| } |
| } |
| if (alreadyLoaded) { |
| fscr.swapInGeneratorsForClassLoader(cl, casImpl); |
| } else { |
| casImpl.setLocalFsGenerators(newFSGeneratorSet); |
| } |
| sharedView.isInitializedForClassLoader.put(cl, Boolean.TRUE); |
| } |
| |
| // note all callers are synchronized |
| private void copyDownSuperGenerators(Map<String, LoadedJCasType<?>> jcasTypes, FSGenerator<?>[] fsGenerators) { |
| final TypeSystem ts = casImpl.getTypeSystem(); |
| Iterator<Type> typeIt = ts.getTypeIterator(); // reset iterator to start |
| Type topType = ts.getTopType(); |
| Type superType = null; |
| while (typeIt.hasNext()) { |
| Type t = typeIt.next(); |
| if (builtInsWithNoJCas.contains(t.getName())) |
| continue; |
| // comment here |
| if (CAS.TYPE_NAME_DOCUMENT_ANNOTATION.equals(t.getName())) { |
| if (jcasTypes.get("org.apache.uima.jcas.tcas.DocumentAnnotation") != null) |
| continue; |
| } else if (builtInsWithAltNames.contains(t.getName())) |
| continue; // because jcasTypes has no entry for these |
| if (null != jcasTypes.get(t.getName())) |
| continue; |
| // we believe that at this point, t is not "top", because top is |
| // always loaded |
| // find closest supertype that has a loaded cover class |
| superType = t; |
| String superTypeName; |
| do { |
| superType = ts.getParent(superType); |
| superTypeName = superType.getName(); |
| if (builtInsWithAltNames.contains(superTypeName)) { |
| superTypeName = "org.apache.uima.jcas." + superTypeName.substring(5); |
| } |
| } while ((null == jcasTypes.get(superTypeName) && !superType.equals(topType))); |
| // copy down its generator |
| fsGenerators[((TypeImpl) t).getCode()] = fsGenerators[((TypeImpl) superType).getCode()]; |
| } |
| } |
| |
| /** |
| * creates a new JCas instance that corresponds to a CAS instance. Will be called once by the UIMA |
| * framework when it creates the CAS. |
| * |
| * @param cas |
| * a CAS instance |
| * @return newly created and initialized JCas |
| * @throws CASException - |
| */ |
| public static JCas getJCas(CASImpl cas) throws CASException { |
| JCasImpl jcas = new JCasImpl(cas); |
| reportInitErrors(jcas); |
| return jcas; |
| } |
| |
| private static void reportInitErrors(JCasImpl jcas) throws CASException { |
| JCasSharedView sv = jcas.sharedView; |
| if (sv.errorSet.size() > 0) { |
| boolean doThrow = false; |
| StringBuffer msg = new StringBuffer(100); |
| msg.append('\n'); |
| for (ErrorReport f : sv.errorSet) { |
| msg.append(f.e.getMessage()); |
| msg.append('\n'); |
| doThrow = doThrow || f.doThrow; |
| } |
| if (doThrow) { |
| CASException e = new CASException(CASException.JCAS_INIT_ERROR, |
| new String[] { msg.toString() }); |
| throw e; |
| } else { |
| Logger logger = UIMAFramework.getLogger(); |
| if (null == logger) { |
| throw new CASRuntimeException(CASException.JCAS_INIT_ERROR, new String[] {msg.toString()}); |
| } else { |
| logger.log(Level.WARNING, msg.toString()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * built-in types which have alternate names It really isn't necessary to skip these - they're |
| * never instantiated. But it is a very slight performance boost, and it may be safer given |
| * possible future changes to these types' implementations. |
| */ |
| private static final Collection<String> builtInsWithNoJCas = new ArrayList<String>(); |
| static { |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_BOOLEAN); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_BYTE); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_SHORT); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_INTEGER); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_LONG); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_FLOAT); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_DOUBLE); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_STRING); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_ARRAY_BASE); |
| builtInsWithNoJCas.add(CAS.TYPE_NAME_LIST_BASE); |
| } |
| |
| private static final Collection<String> builtInsWithAltNames = new ArrayList<String>(); |
| static { // initialization code |
| builtInsWithAltNames.add(CAS.TYPE_NAME_TOP); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_STRING_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_BOOLEAN_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_BYTE_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_SHORT_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_INTEGER_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_LONG_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_FS_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_FLOAT_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_DOUBLE_ARRAY); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_EMPTY_FLOAT_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_EMPTY_FS_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_EMPTY_INTEGER_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_EMPTY_STRING_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_FLOAT_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_FS_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_INTEGER_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_STRING_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_NON_EMPTY_FLOAT_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_NON_EMPTY_FS_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_NON_EMPTY_INTEGER_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_NON_EMPTY_STRING_LIST); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_SOFA); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_ANNOTATION_BASE); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_ANNOTATION); |
| builtInsWithAltNames.add(CAS.TYPE_NAME_DOCUMENT_ANNOTATION); |
| } |
| |
| // This generator class is used in place of the generator that is in each of the |
| // older JCasGen'd classes |
| |
| // It makes use of the passed-in CAS view so one generator works for all Cas Views. |
| // It references the xxx_Type instance using the getType call, which will |
| // (lazily) instantiate the xxx_Type object if needed (due to switching class loaders: |
| // see comment under getType(int)) |
| |
| static private class JCasFsGenerator<T extends TOP> implements FSGenerator<T> { |
| // multiple reader threads in same CAS |
| static final ThreadLocal<Object[]> initArgsThreadLocal = new ThreadLocal<Object[]>() { |
| protected Object[] initialValue() { return new Object[2]; } }; |
| |
| private final int type; |
| |
| private final Constructor<T> c; |
| |
| private final boolean isSubtypeOfAnnotationBase; |
| |
| private final int sofaNbrFeatCode; |
| |
| private final int annotSofaFeatCode; |
| |
| |
| JCasFsGenerator(int type, Constructor<T> c, boolean isSubtypeOfAnnotationBase, |
| int sofaNbrFeatCode, int annotSofaFeatCode) { |
| this.type = type; |
| this.c = c; |
| this.isSubtypeOfAnnotationBase = isSubtypeOfAnnotationBase; |
| this.sofaNbrFeatCode = sofaNbrFeatCode; |
| this.annotSofaFeatCode = annotSofaFeatCode; |
| } |
| |
| /* |
| * Called from the CAS's this.svd.localFsGenerators |
| * |
| * Those are set up with either JCas style generators, or the |
| * the shared common instances of FeatureStructureImplC for non-JCas classes. |
| * |
| */ |
| // Called in 3 cases |
| // 1) a non-JCas call to create a new JCas style FS |
| // 2) a dereference of an existing FS |
| // 3) an iterator |
| public T createFS(int addr, CASImpl casView) { |
| try { |
| JCasImpl jcasView = (JCasImpl) casView.getJCas(); |
| T fs = jcasView.<T>getJfsFromCaddr(addr); |
| if (null != fs) { |
| fs.jcasType = jcasView.getType(type); |
| return fs; |
| } |
| return doCreateFS(addr, casView); |
| } catch (CASException e1) { |
| logAndThrow(e1, null); |
| return null; // to avoid compile warning |
| } |
| } |
| |
| private T doCreateFS(int addr, CASImpl casView) { |
| // this funny logic is because although the annotationView should always be set if |
| // a type is a subtype of annotation, it isn't always set if an application uses low-level |
| // api's. Rather than blow up, we limp along. |
| CASImpl maybeAnnotationView = null; |
| if (isSubtypeOfAnnotationBase) { |
| final int sofaNbr = getSofaNbr(addr, casView); |
| if (sofaNbr > 0) { |
| maybeAnnotationView = (CASImpl) casView.getView(sofaNbr); |
| } |
| } |
| final CASImpl view = (null != maybeAnnotationView) ? maybeAnnotationView : casView; |
| |
| try { |
| JCasImpl jcasView = (JCasImpl) view.getJCas(); |
| final Object[] initargs = initArgsThreadLocal.get(); |
| initargs[0] = Integer.valueOf(addr); |
| initargs[1] = jcasView.getType(type); |
| T fs = null; |
| try { |
| fs = (T) c.newInstance(initargs); |
| } catch (IllegalArgumentException e) { |
| logAndThrow(e, jcasView); |
| } catch (InstantiationException e) { |
| logAndThrow(e, jcasView); |
| } catch (IllegalAccessException e) { |
| logAndThrow(e, jcasView); |
| } catch (InvocationTargetException e) { |
| logAndThrow(e, jcasView); |
| } |
| jcasView.putJfsFromCaddr(addr, fs); |
| return fs; |
| } catch (CASException e1) { |
| logAndThrow(e1, null); |
| return null; |
| } |
| } |
| |
| private void logAndThrow(Exception e, JCasImpl jcasView) { |
| CASRuntimeException casEx = new CASRuntimeException( |
| CASRuntimeException.JCAS_CAS_MISMATCH, |
| new String[] { (null == jcasView) ? "-- ignore outer msg, error is can''t get value of jcas from cas" |
| : (jcasView.getType(type).casType.getName() + "; exception= " |
| + e.getClass().getName() + "; msg= " + e.getLocalizedMessage()) }); |
| casEx.initCause(e); |
| throw casEx; |
| } |
| |
| private int getSofaNbr(final int addr, final CASImpl casView) { |
| final int sofa = casView.ll_getIntValue(addr, annotSofaFeatCode, false); |
| return (sofa == 0) ? 0 : casView.ll_getIntValue(sofa, sofaNbrFeatCode); |
| } |
| } |
| |
| // per JCas instance - so don't need to synch. |
| private final Object[] constructorArgsFor_Type = new Object[2]; |
| |
| /** |
| * Make the instance of the JCas xxx_Type class for this CAS. Note: not all types will have |
| * xxx_Type. Instance creation does the typeSystemInit kind of function, as well. |
| * |
| * @param jcasTypeInfo - |
| * @param alreadyLoaded - |
| * @param fsGenerators updated by side effect with new instances of the _Type class |
| * @return true if a new instance of a _Type class was created |
| */ |
| private <T extends TOP> boolean makeInstanceOf_Type(LoadedJCasType<T> jcasTypeInfo, boolean alreadyLoaded, |
| FSGenerator<?>[] fsGenerators) { |
| |
| // return without doing anything if the _Type instance is already existing |
| // this happens when a JCas has some _Type instances made (e.g, the |
| // built-in ones) but the class loader was switched. Some of the |
| // _Type instances for the new class loader can share previously |
| // instantiated _Type instances, but others may be different |
| // (due to different impls of the _Type class loaded by the different |
| // class loader). |
| // This can also happen in the case where JCasImpl.getType is called for a non-existing class. |
| // What happens in this case is that the getType code has to assume that |
| // perhaps none of the _Type instances were made for this JCas (yet), because |
| // these are created lazily - so it calls instantiateJCas_Types to make them. |
| // If they were already made, this next test short circuits this. |
| int typeIndex = jcasTypeInfo.index; |
| if (typeArray[typeIndex] != null) { |
| return false; |
| } |
| |
| Constructor<?> c_Type = jcasTypeInfo.constructorFor_Type; |
| Constructor<T> cType = jcasTypeInfo.constructorForType; |
| TypeImpl casType = (TypeImpl) casImpl.getTypeSystem().getType(jcasTypeInfo.typeName); |
| |
| try { |
| constructorArgsFor_Type[0] = this; |
| constructorArgsFor_Type[1] = casType; |
| TOP_Type x_Type_instance = (TOP_Type) c_Type.newInstance(constructorArgsFor_Type); |
| typeArray[typeIndex] = x_Type_instance; |
| // install the standard generator |
| // this is sharable by all views, since the CAS is passed to the generator |
| // Also sharable by all in a CasPool, except for "swapping" due to PEARs/Classloaders. |
| if (!alreadyLoaded) { |
| final TypeSystemImpl ts = casImpl.getTypeSystemImpl(); |
| fsGenerators[casType.getCode()] = new JCasFsGenerator<T>(typeIndex, cType, |
| jcasTypeInfo.isSubtypeOfAnnotationBase, TypeSystemImpl.sofaNumFeatCode, TypeSystemImpl.annotSofaFeatCode); |
| // this.casImpl.getFSClassRegistry().loadJCasGeneratorForType(typeIndex, cType, casType, |
| // jcasTypeInfo.isSubtypeOfAnnotationBase); |
| } |
| } catch (SecurityException e) { |
| logAndThrow(e); |
| } catch (InstantiationException e) { |
| logAndThrow(e); |
| } catch (IllegalAccessException e) { |
| logAndThrow(e); |
| } catch (InvocationTargetException e) { |
| logAndThrow(e); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| logAndThrow(e); |
| } |
| return true; |
| } |
| |
| /** |
| * Make the instance of the JCas xxx_Type class for this CAS. Note: not all types will have |
| * xxx_Type. Instance creation does the typeSystemInit kind of function, as well. |
| */ |
| /* |
| * private void makeInstanceOf_Type(Type casType, Class clas, CASImpl cas) { Constructor c; Field |
| * typeIndexField = null; int typeIndex; try { c = clas.getDeclaredConstructor(jcasBaseAndType); |
| * try { |
| * |
| * typeIndexField = clas.getDeclaredField("typeIndexID"); } catch (NoSuchFieldException e) { try { // |
| * old version has the index in the base type String name = clas.getName(); Class clas2 = |
| * Class.forName(name.substring(0, name.length() - 5), true, cas .getJCasClassLoader()); // drop |
| * _Type typeIndexField = clas2.getDeclaredField("typeIndexID"); } catch (NoSuchFieldException e2) { |
| * logAndThrow(e2); } catch (ClassNotFoundException e3) { logAndThrow(e3); } } typeIndex = |
| * typeIndexField.getInt(null); // null - static instance var TOP_Type x_Type_instance = |
| * (TOP_Type) c.newInstance(new Object[] { this, casType }); typeArray[typeIndex] = |
| * x_Type_instance; } catch (SecurityException e) { logAndThrow(e); } catch (NoSuchMethodException |
| * e) { logAndThrow(e); } catch (InstantiationException e) { logAndThrow(e); } catch |
| * (IllegalAccessException e) { logAndThrow(e); } catch (InvocationTargetException e) { |
| * logAndThrow(e); } catch (ArrayIndexOutOfBoundsException e) { logAndThrow(e); } } |
| */ |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getRequiredType(java.lang.String) |
| */ |
| public Type getRequiredType(String s) throws CASException { |
| Type t = getTypeSystem().getType(s); |
| if (null == t) { |
| CASException casEx = new CASException(CASException.JCAS_TYPENOTFOUND_ERROR, |
| new String[] { s }); |
| throw casEx; |
| } |
| return t; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getRequiredFeature(org.apache.uima.cas.Type, java.lang.String) |
| */ |
| public Feature getRequiredFeature(Type t, String s) throws CASException { |
| Feature f = t.getFeatureByBaseName(s); |
| if (null == f) { |
| CASException casEx = new CASException(CASException.JCAS_FEATURENOTFOUND_ERROR, new String[] { |
| t.getName(), s }); |
| throw casEx; |
| } |
| return f; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getRequiredFeatureDE(org.apache.uima.cas.Type, java.lang.String, |
| * java.lang.String, boolean) |
| */ |
| |
| public Feature getRequiredFeatureDE(Type t, String s, String rangeName, boolean featOkTst) { |
| Feature f = t.getFeatureByBaseName(s); |
| Type rangeType = this.getTypeSystem().getType(rangeName); |
| if (null == f && featOkTst) { |
| CASException casEx = new CASException(CASException.JCAS_FEATURENOTFOUND_ERROR, new String[] { |
| t.getName(), s }); |
| sharedView.errorSet.add(new ErrorReport(casEx, false)); // false - no throw |
| } |
| if (null != f) |
| try { |
| casImpl.checkTypingConditions(t, rangeType, f); |
| } catch (LowLevelException e) { |
| CASException casEx = new CASException(CASException.JCAS_FEATURE_WRONG_TYPE, new String[] { |
| t.getName(), s, rangeName, f.getRange().toString() }); |
| sharedView.errorSet.add(new ErrorReport(casEx, true)); |
| } |
| return f; |
| } |
| |
| /** |
| * Internal - throw missing feature exception at runtime |
| * |
| * @param feat - |
| * @param type - |
| */ |
| public void throwFeatMissing(String feat, String type) { |
| CASRuntimeException e = new CASRuntimeException(CASRuntimeException.INAPPROP_FEAT, |
| new String[] { feat, type }); |
| throw e; |
| } |
| |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#putJfsFromCaddr(int, org.apache.uima.cas.FeatureStructure) |
| */ |
| public void putJfsFromCaddr(int casAddr, FeatureStructure fs) { |
| sharedView.cAddr2Jfs.put((FeatureStructureImpl) fs); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * Generics: extends FeatureStructure, not TOP, because |
| * when the JCas is being used, but a particular type instance doesn't have a JCas cover class, |
| * this holds instances of FeatureStructureC - the shared Class for non-JCas Java cover objects. |
| * |
| * @see org.apache.uima.jcas.JCas#getJfsFromCaddr(int) |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends TOP> T getJfsFromCaddr(int casAddr) { |
| return (T) sharedView.cAddr2Jfs.getReserve(casAddr); |
| } |
| |
| public void showJfsFromCaddrHistogram() { |
| sharedView.cAddr2Jfs.showHistogram(); |
| } |
| |
| // * Implementation of part of the Cas interface as part of JCas* |
| |
| /* |
| * (Internal Use only) called by the CAS reset function - clears the hashtable holding the |
| * associations. |
| */ |
| public static void clearData(CAS cas) { |
| JCasImpl jcas = (JCasImpl) ((CASImpl) cas).getExistingJCas(); |
| final JCasSharedView sv = jcas.sharedView; |
| sv.stringArray0L = null; |
| sv.floatArray0L = null; |
| sv.fsArray0L = null; |
| sv.integerArray0L = null; |
| |
| // JCasHashMap currentMap = sv.cAddr2JfsByClassLoader.get( ((CASImpl) cas).getJCasClassLoader() ); |
| for (Iterator<Map.Entry<ClassLoader, JCasHashMap>> it = sv.cAddr2JfsByClassLoader.entrySet().iterator(); it.hasNext();) { |
| Map.Entry<ClassLoader, JCasHashMap> e = it.next(); |
| e.getValue().clear(); // implements resize as well |
| ClassLoader cl = e.getKey(); |
| JCasHashMap jcas_hashmap = e.getValue(); |
| jcas_hashmap.clear(); |
| if (cl instanceof UIMAClassLoader) { |
| if (((UIMAClassLoader) cl).isClosed()) { |
| it.remove(); |
| if (sv.cAddr2Jfs == jcas_hashmap) { |
| sv.cAddr2Jfs = null; |
| } |
| } |
| } |
| |
| // sv.cAddr2Jfs = e.getValue(); |
| // sv.cAddr2Jfs.clear(); // implements resize as well |
| // if (! e.getValue().equals(currentMap)) { |
| // it.remove(); |
| // } |
| } |
| // sv.cAddr2Jfs = sv.cAddr2JfsByClassLoader |
| // .get(((CASImpl) cas).getJCasClassLoader()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#reset() |
| */ |
| public void reset() { |
| casImpl.reset(); |
| } |
| |
| private final static int NULL = 0; |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#checkArrayBounds(int, int) |
| */ |
| public final void checkArrayBounds(int fsRef, int pos) { |
| if (NULL == fsRef) { |
| LowLevelException e = new LowLevelException(LowLevelException.NULL_ARRAY_ACCESS); |
| // note - need to add this to ll_runtimeException |
| e.addArgument(Integer.toString(pos)); |
| throw e; |
| } |
| final int arrayLength = casImpl.ll_getArraySize(fsRef); |
| if (pos < 0 || pos >= arrayLength) { |
| LowLevelException e = new LowLevelException(LowLevelException.ARRAY_INDEX_OUT_OF_RANGE); |
| e.addArgument(Integer.toString(pos)); |
| throw e; |
| } |
| } |
| |
| // ***************** |
| // * Sofa support * |
| // ***************** |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofa(org.apache.uima.cas.SofaID) |
| */ |
| public Sofa getSofa(SofaID sofaID) { |
| return (Sofa) casImpl.getSofa(sofaID); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofa() |
| */ |
| public Sofa getSofa() { |
| return (Sofa) casImpl.getSofa(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#createView(java.lang.String) |
| */ |
| public JCas createView(String sofaID) throws CASException { |
| return casImpl.createView(sofaID).getJCas(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getJCas(org.apache.uima.jcas.cas.Sofa) |
| */ |
| public JCas getJCas(Sofa sofa) throws CASException { |
| return casImpl.getView(sofa).getJCas(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaIterator() |
| */ |
| public FSIterator<SofaFS> getSofaIterator() { |
| return casImpl.getSofaIterator(); |
| } |
| |
| // ***************** |
| // * Index support * |
| // ***************** |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getJFSIndexRepository() |
| */ |
| public JFSIndexRepository getJFSIndexRepository() { |
| return jfsIndexRepository; |
| } |
| |
| // **************** |
| // * TCas support * |
| // **************** |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getDocumentAnnotationFs() |
| */ |
| public TOP getDocumentAnnotationFs() { |
| return (TOP) casImpl.getDocumentAnnotation(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getDocumentText() |
| */ |
| public String getDocumentText() { |
| return casImpl.getDocumentText(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaDataString() |
| */ |
| public String getSofaDataString() { |
| return casImpl.getSofaDataString(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaDataArray() |
| */ |
| public FeatureStructure getSofaDataArray() { |
| return casImpl.getSofaDataArray(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaDataURI() |
| */ |
| public String getSofaDataURI() { |
| return casImpl.getSofaDataURI(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaMimeType() |
| */ |
| public String getSofaMimeType() { |
| return casImpl.getSofaMimeType(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setDocumentText(java.lang.String) |
| */ |
| public void setDocumentText(String text) throws CASRuntimeException { |
| casImpl.setDocumentText(text); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setSofaDataString(java.lang.String, java.lang.String) |
| */ |
| public void setSofaDataString(String text, String mime) throws CASRuntimeException { |
| casImpl.setSofaDataString(text, mime); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setSofaDataArray(org.apache.uima.jcas.cas.TOP, java.lang.String) |
| */ |
| public void setSofaDataArray(FeatureStructure array, String mime) throws CASRuntimeException { |
| casImpl.setSofaDataArray(array, mime); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setSofaDataURI(java.lang.String, java.lang.String) |
| */ |
| public void setSofaDataURI(String uri, String mime) throws CASRuntimeException { |
| casImpl.setSofaDataURI(uri, mime); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getDocumentLanguage() |
| */ |
| public String getDocumentLanguage() { |
| return casImpl.getDocumentLanguage(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setDocumentLanguage(java.lang.String) |
| */ |
| public void setDocumentLanguage(String language) throws CASRuntimeException { |
| casImpl.setDocumentLanguage(language); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getSofaDataStream() |
| */ |
| public InputStream getSofaDataStream() { |
| return casImpl.getSofaDataStream(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getConstraintFactory() |
| */ |
| public ConstraintFactory getConstraintFactory() { |
| return casImpl.getConstraintFactory(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#createFeaturePath() |
| */ |
| public FeaturePath createFeaturePath() { |
| return casImpl.createFeaturePath(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#createFilteredIterator(org.apache.uima.cas.FSIterator, |
| * org.apache.uima.cas.FSMatchConstraint) |
| */ |
| public <T extends FeatureStructure> FSIterator<T> createFilteredIterator(FSIterator<T> it, FSMatchConstraint constraint) { |
| return casImpl.createFilteredIterator(it, constraint); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getStringArray0L() |
| */ |
| |
| public StringArray getStringArray0L() { |
| if (null == sharedView.stringArray0L) |
| sharedView.stringArray0L = new StringArray(this, 0); |
| return sharedView.stringArray0L; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getIntegerArray0L() |
| */ |
| public IntegerArray getIntegerArray0L() { |
| if (null == sharedView.integerArray0L) |
| sharedView.integerArray0L = new IntegerArray(this, 0); |
| return sharedView.integerArray0L; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getFloatArray0L() |
| */ |
| public FloatArray getFloatArray0L() { |
| if (null == sharedView.floatArray0L) |
| sharedView.floatArray0L = new FloatArray(this, 0); |
| return sharedView.floatArray0L; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getFSArray0L() |
| */ |
| public FSArray getFSArray0L() { |
| if (null == sharedView.fsArray0L) |
| sharedView.fsArray0L = new FSArray(this, 0); |
| return sharedView.fsArray0L; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#processInit() |
| */ |
| public void processInit() { |
| // unused |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.AbstractCas_ImplBase#setOwn |
| */ |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#setOwner(org.apache.uima.cas.CasOwner) |
| */ |
| public void setOwner(CasOwner aCasOwner) { |
| casImpl.setOwner(aCasOwner); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#release() |
| */ |
| public void release() { |
| casImpl.release(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getView(java.lang.String) |
| */ |
| public JCas getView(String localViewName) throws CASException { |
| // try { // defer because can break some use cases see UIMA-5869 |
| return casImpl.getView(localViewName).getJCas(); |
| // } catch (CASRuntimeException e) { |
| // throw new CASException(e); // https://issues.apache.org/jira/browse/UIMA-5869 |
| // } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getView(org.apache.uima.cas.SofaFS) |
| */ |
| public JCas getView(SofaFS aSofa) throws CASException { |
| return casImpl.getView(aSofa).getJCas(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#addFsToIndexes(org.apache.uima.cas.FeatureStructure) |
| */ |
| public void addFsToIndexes(FeatureStructure instance) { |
| casImpl.addFsToIndexes(instance); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#removeFsFromIndexes(org.apache.uima.cas.FeatureStructure) |
| */ |
| public void removeFsFromIndexes(FeatureStructure instance) { |
| casImpl.removeFsFromIndexes(instance); |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.apache.uima.jcas.JCas#removeAllIncludingSubtypes(int) |
| */ |
| public void removeAllIncludingSubtypes(int i) { |
| getFSIndexRepository().removeAllIncludingSubtypes(getCasType(i)); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.uima.jcas.JCas#removeAllExcludingSubtypes(int) |
| */ |
| public void removeAllExcludingSubtypes(int i) { |
| getFSIndexRepository().removeAllExcludingSubtypes(getCasType(i)); |
| } |
| |
| /** |
| * @see org.apache.uima.cas.CAS#fs2listIterator(FSIterator) |
| */ |
| public <T extends FeatureStructure> ListIterator<T> fs2listIterator(FSIterator<T> it) { |
| return casImpl.fs2listIterator(it); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.BaseCas#createFeatureValuePath(java.lang.String) |
| */ |
| public FeatureValuePath createFeatureValuePath(String featureValuePath) |
| throws CASRuntimeException { |
| return casImpl.createFeatureValuePath(featureValuePath); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.BaseCas#createSofa(org.apache.uima.cas.SofaID, java.lang.String) |
| * @deprecated |
| */ |
| public SofaFS createSofa(SofaID sofaID, String mimeType) { |
| // extract absolute SofaName string from the ID |
| return casImpl.createSofa(sofaID, mimeType); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.BaseCas#getIndexRepository() |
| */ |
| public FSIndexRepository getIndexRepository() { |
| return casImpl.getIndexRepository(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.BaseCas#getViewName() |
| */ |
| public String getViewName() { |
| return casImpl.getViewName(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.cas.BaseCas#size() |
| */ |
| public int size() { |
| // TODO improve this to account for JCas |
| // structure sizes |
| return casImpl.size(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getAnnotationIndex() |
| */ |
| public AnnotationIndex<Annotation> getAnnotationIndex() { |
| return casImpl.<Annotation>getAnnotationIndex(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getAnnotationIndex(org.apache.uima.cas.Type) |
| */ |
| @Override |
| public <T extends Annotation> AnnotationIndex<T> getAnnotationIndex(Type type) throws CASRuntimeException { |
| return (AnnotationIndex<T>) casImpl.<T>getAnnotationIndex(type); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getAnnotationIndex(int) |
| */ |
| public <T extends Annotation> AnnotationIndex<T> getAnnotationIndex(int type) throws CASRuntimeException { |
| return (AnnotationIndex<T>) casImpl.<T>getAnnotationIndex(this.getCasType(type)); |
| } |
| |
| public <T extends Annotation> AnnotationIndex<T> getAnnotationIndex(Class<T> clazz) { |
| return getAnnotationIndex(getCasType(clazz)); |
| } |
| |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getViewIterator() |
| */ |
| public Iterator<JCas> getViewIterator() throws CASException { |
| List<JCas> viewList = new ArrayList<JCas>(); |
| Iterator<CAS> casViewIter = casImpl.getViewIterator(); |
| while (casViewIter.hasNext()) { |
| viewList.add((casViewIter.next()).getJCas()); |
| } |
| return viewList.iterator(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.uima.jcas.JCas#getViewIterator(java.lang.String) |
| */ |
| public Iterator<JCas> getViewIterator(String localViewNamePrefix) throws CASException { |
| List<JCas> viewList = new ArrayList<JCas>(); |
| Iterator<CAS> casViewIter = casImpl.getViewIterator(localViewNamePrefix); |
| while (casViewIter.hasNext()) { |
| viewList.add((casViewIter.next()).getJCas()); |
| } |
| return viewList.iterator(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.uima.jcas.JCas#protectIndexes() |
| */ |
| @Override |
| public AutoCloseable protectIndexes() { |
| return casImpl.protectIndexes(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.uima.jcas.JCas#protectIndexes(java.lang.Runnable) |
| */ |
| @Override |
| public void protectIndexes(Runnable runnable) { |
| casImpl.protectIndexes(runnable); |
| } |
| |
| /** |
| * Static method to get the corresponding Type for a JCas class object |
| */ |
| private int getTypeRegistryIndex(Class<? extends TOP> clazz) { |
| try { |
| return clazz.getField("type").getInt(clazz); |
| } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { |
| throw new RuntimeException(e); // should never happen |
| } |
| } |
| |
| /** |
| * Return the UIMA Type object corresponding to this JCas's JCas cover class |
| * (Note: different JCas's, with different type systems, may share the same cover class impl) |
| * @param clazz a JCas cover class |
| * @return the corresponding UIMA Type object |
| */ |
| public Type getCasType(Class<? extends TOP> clazz) { |
| return getCasType(getTypeRegistryIndex(clazz)); |
| } |
| |
| @Override |
| public <T extends TOP> FSIterator<T> getAllIndexedFS(Class<T> clazz) { |
| return getFSIndexRepository().getAllIndexedFS(getCasType(clazz)); |
| } |
| |
| @Override |
| public <T extends TOP> FSIndex<T> getIndex(String label, Class<T> clazz) { |
| return getFSIndexRepository().getIndex(label, getCasType(clazz)); |
| } |
| } |