You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2014/09/08 23:12:56 UTC
svn commit: r1623582 [2/4] - in /uima/uimaj/trunk/uimaj-core/src:
main/java/org/apache/uima/cas/impl/ test/java/org/apache/uima/cas/impl/
test/resources/CasSerialization/expected/
Added: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/JsonCasSerializer.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/JsonCasSerializer.java?rev=1623582&view=auto
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/JsonCasSerializer.java (added)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/JsonCasSerializer.java Mon Sep 8 21:12:55 2014
@@ -0,0 +1,1212 @@
+/*
+ * 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.IOException;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.uima.cas.ByteArrayFS;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.Marker;
+import org.apache.uima.cas.Type;
+import org.apache.uima.cas.TypeSystem;
+import org.apache.uima.cas.impl.CasSerializerSupport.CasDocSerializer;
+import org.apache.uima.cas.impl.CasSerializerSupport.CasSerializerSupportSerialize;
+import org.apache.uima.cas.impl.XmiSerializationSharedData.OotsElementData;
+import org.apache.uima.cas.impl.XmiSerializationSharedData.XmiArrayElement;
+import org.apache.uima.internal.util.XmlAttribute;
+import org.apache.uima.internal.util.XmlElementName;
+import org.apache.uima.internal.util.XmlElementNameAndContents;
+import org.apache.uima.util.JsonContentHandlerJacksonWrapper;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+/**
+ * CAS serializer for JSON formats.
+ * Writes a CAS in one of several JSON formats.
+ *
+ * Delta serialization, which depends on a previous deserialization, is not supported (yet)
+ * because JSON deserialization is not supported.
+ *
+ * To use,
+ * - create an instance of this class,
+ * - optionally) configure the instance, and then
+ * - call serialize on the instance, optionally passing in additional parameters.
+ *
+ * After the 1st 2 steps, the serializer instance may be used for multiple calls (on multiple threads) to
+ * the 3rd serialize step, if all calls use the same configuration.
+ *
+ * There are "convenience" static serialize methods that do these three steps for common configurations.
+ *
+ * Parameters can be configured in this instance (I), and/or as part of the serialize(S) call.
+ *
+ * The parameters that can be configured are:
+ * <ul>
+ * <li>(S) The CAS to serialize
+ * <li>(S) where to put the output - an OutputStream, Writer, or File</li>
+ * <li>(I,S) a type system - (default null) if supplied, it is used to "filter" types and features that are serialized. If provided, only
+ * those that exist in the passed in type system are included in the serialization</li>
+ * <li>(I,S) a flag for prettyprinting - default false (no prettyprinting)</li>
+ * </ul>
+ *
+ * For Json serialization, additional configuration from the Jackson implementation can be configured
+ * on 2 associated Jackson instances:
+ * - JsonFactory
+ * - JsonGenerator
+ * using the standard Jackson methods on the associated JsonFactory instance;
+ * see the Jackson JsonFactory and JsonGenerator javadocs for details.
+ *
+ * These 2 Jackson objects are settable/gettable from an instance of this class.
+ * They are created if not supplied by the caller.
+ *
+ * Once this instance is configured, the serialize method is called
+ * to serialized a CAS to an output.
+ *
+ * Instances of this class must be used on only one thread while configuration is being done;
+ * afterwards, multiple threads may use the configured instance, to call serialize.
+ */
+public class JsonCasSerializer {
+
+ private static final SerializedString FEATURE_REFS_NAME = new SerializedString("@featureRefs");
+
+ private static final SerializedString SUPER_TYPES_NAME = new SerializedString("@superTypes");
+
+ private static final SerializedString JSON_CONTEXT_TAG_LOCAL_NAME = new SerializedString("@context");
+
+ private static final SerializedString JSON_CAS_FEATURE_STRUCTURES = new SerializedString("@cas_feature_structures");
+
+ private static final SerializedString JSON_CAS_VIEWS = new SerializedString("@cas_views");
+
+ private static final SerializedString JSON_ID_ATTR_NAME = new SerializedString("@id");
+
+ private static final SerializedString JSON_TYPE_ATTR_NAME = new SerializedString("@type");
+
+ private static final SerializedString JSON_COLLECTION_ATTR_NAME = new SerializedString("@collection");
+
+ private static final SerializedString JSON_VIEW_ADDED = new SerializedString("added_members");
+ private static final SerializedString JSON_VIEW_DELETED = new SerializedString("deleted_members");
+ private static final SerializedString JSON_VIEW_REINDEXED = new SerializedString("reindexed_members");
+
+ /**
+ * This enum describes the kinds of JSON formats used for serializing Feature Structures
+ * An individual Feature Structure is serialized as a JSON object, with feature-value pairs.
+ *
+ * The "type" and "id" are usually embedded as extra special features in the list of features, but
+ * if that is not wanted, they can be turned off:
+ *
+ * OMIT_ID
+ * OMIT_TYPE
+ *
+ * The type or ID can optionally be used as an key in an enclosing map, to make accessing things
+ * by these keys straightforward. If you want the serialization to include an enclosing map,
+ * you pick from one (not both) of these:
+ *
+ * INDEX_ID
+ * INDEX_TYPE
+ *
+ * Without the enclosing map, the collection of feature structures is serialized as a JSON array of
+ * feature structure objects.
+ *
+ * With the enclosing map, the key is the value of the index item, and the values are either
+ *
+ * a JSON ARRAY of feature structure objects, for KEY_BY_TYPE,
+ * since there can be multiple feature structures of a particular type, or
+ *
+ * one feature structure object, for KEY_BY_ID, since each feature structure has a unique ID
+ *
+ * In the case of KEY_BY_TYPE, the feature structures are ordered like the UIMA built-in annotation index.
+ *
+ * Example of INDEX_ID:
+ * { "123" : { "@id" : 123, "@type" : "type-name", feat : value, ... }
+ *
+ * Example of INDEX_TYPE:
+ * { "type-name" : [ { "@id" : 123, "@type" : "type-name", feat : value ... },
+ * { "@id" : 456, "@type" : "type-name", feat : value ... }
+ * ...
+ * ],
+ * ...
+ * }
+ */
+ public enum JsonCasFormat {
+ OMIT_TYPE,
+ OMIT_ID,
+ INDEX_TYPE, // outputs each FS as "nnn" : { "@type" : "foo", features : values ... }
+ INDEX_ID, // outputs each FS as "type" : { "@id" : 123, features : values ... }
+ }
+
+ /**
+ * The serialization can optionally include context information in addition to the feature structures.
+ *
+ * This context information is specified, per used-type.
+ *
+ * It can be further subdivided into 3 parts:
+ * 1) what their super types are. This is needed in case the receiver needs to iterate over
+ * a supertype (and all of its subtypes), e.g. an interator over all "Annotations".
+ * 2) which of their features are references to other feature structures. This is needed
+ * if the receiver wants to construct "graphs" of feature structures, with arbitrary links, back-links, cycles, etc.
+ * 3) whether or not to include the map from short type names to their fully qualified equivalents.
+ *
+ */
+ public enum JsonContextFormat {
+ omitContext, includeContext,
+ omitSupertypes, includeSuperTypes,
+ omitFeatureRefs, includeFeatureRefs,
+ omitExpandedTypeNames, includeExpandedTypeNames,
+ }
+
+ private final CasSerializerSupport css = new CasSerializerSupport();
+
+ private JsonFactory jsonFactory = null;
+
+ private final EnumSet<JsonCasFormat> jsonCasFormat = EnumSet.noneOf(JsonCasFormat.class);
+
+ private boolean isWithContext = true;
+ private boolean isWithSupertypes = true;
+ private boolean isWithFeatureRefs = true;
+ private boolean isWithExpandedTypeNames = true;
+ private boolean isWithViews = true;
+ private boolean isOmitDefaultValues = true;
+
+
+ /***********************************************
+ * C O N S T R U C T O R S *
+ ***********************************************/
+
+ /**
+ * Creates a new XmiCasSerializer
+ */
+ public JsonCasSerializer() {
+ }
+
+ /**************************************************
+ * J S O N *
+ **************************************************/
+
+ /****************************************************
+ * Static JSON Serializer methods for convenience *
+ * *
+ * Note: these are named jsonSerialize *
+ * The non-static methods *
+ * are named serializeJson *
+ ****************************************************/
+
+ /**
+ * Serializes a CAS using JSON
+ *
+ * @param aCAS
+ * CAS to serialize.
+ * @param output
+ * a File, OutputStream or Writer to which to write the XMI document
+ *
+ * @throws IOException if there was an IOException
+ */
+ public static void jsonSerialize(CAS aCAS, Object output) throws IOException {
+ jsonSerialize(aCAS, null, output, false, null);
+ }
+
+ /**
+ * Serializes a CAS to an output (File, OutputStream, XMI stream, or Writer).
+ * The supplied typesystem filters the output
+ *
+ * @param aCAS
+ * CAS to serialize.
+ * @param aTargetTypeSystem
+ * type system used for filtering what gets serialized. Any types or features not in the
+ * target type system will not be serialized. A null value indicates no filtering, that is,
+ * that all types and features will be serialized.
+ * @param output
+ * output (File, OutputStream, or Writer) to which to write the JSON document
+ *
+ * @throws IOException if there was an IOException
+ */
+ public static void jsonSerialize(CAS aCAS, TypeSystem aTargetTypeSystem, Object output)
+ throws IOException {
+ jsonSerialize(aCAS, aTargetTypeSystem, output, false, null);
+ }
+
+ /**
+ * Serializes a Delta CAS to an output (File, Writer, or OutputStream).
+ * This version of this method allows many options to be configured.
+ *
+ *
+ * @param aCAS
+ * CAS to serialize.
+ * @param aTargetTypeSystem
+ * type system to which the produced XMI will conform. Any types or features not in the
+ * target type system will not be serialized. A null value indicates that all types and features
+ * will be serialized.
+ * @param output
+ * File, Writer, or OutputStream to which to write the JSON document
+ * @param aPrettyPrint
+ * if true the JSON output will be formatted with newlines and indenting. If false it will be unformatted.
+ * @param aSharedData
+ * an optional container for data that is shared between the {@link JsonCasSerializer} and the {@link XmiCasDeserializer}.
+ * See the JavaDocs for {@link XmiSerializationSharedData} for details.
+ * @param aMarker
+ * an optional object used to determine which FeatureStructures and modifications were created after
+ * the mark was set. Used to serialize a Delta CAS consisting of only new FSs and views and
+ * preexisting FSs and Views that have been modified. If null, full serialization is done.
+ * See the JavaDocs for {@link Marker} for details.
+ * @throws IOException if there was an IOException
+ */
+ public static void jsonSerialize(CAS aCAS, TypeSystem aTargetTypeSystem, Object output, boolean aPrettyPrint, Marker aMarker)
+ throws IOException {
+ JsonCasSerializer ser = new JsonCasSerializer();
+ ser.setFilterTypes((TypeSystemImpl)aTargetTypeSystem);
+ ser.setPrettyPrint(aPrettyPrint);
+ ser.setDeltaCas(aMarker);
+ ser.serialize(aCAS, output);
+ }
+
+ /*************************************************************************************
+ * Multi-step api
+ *
+ * 1) Create an instance of this class and use for configuration, specifying or defaulting
+ * type system to use for filtering (default - no filtering)
+ * prettyprinting (default - false)
+ *
+ * 1b) Do any additional wanted configuration on the instance of this class
+ * instance.prettyPrint(true/false);
+ * instance.useJsonFactory(factory)
+ * instance.filterTypes(typeSystem)
+ * instance.errorHandler(errorHandler)
+ * instance.jsonFormat(EnumSet.of(x, y, z)) - default is none of the settings
+ *
+ * instance.getGenerator() to further configure the generator if the defaults are not what is wanted.
+ *
+ * 2) call its serializeJson method, passing in the CAS, and an output (Writer/Outputstream/File)
+ *
+ *************************************************************************************/
+
+ /**
+ * Serialize a Cas to an Output, using configurations set on this instance.
+ * Constructs a JsonContentHandlerJacksonWrapper, using configured JsonFactory and prettyprint settings if any
+ * @param cas - the CAS to serialize
+ * @param output - where the output goes, an OutputStream, Writer, or File
+ * @throws IOException if there was an IOException
+ */
+ public void serialize(CAS cas, Object output) throws IOException {
+ JsonContentHandlerJacksonWrapper jch;
+ try {
+ jch = new JsonContentHandlerJacksonWrapper(jsonFactory, output, css.isFormattedOutput);
+ } catch (SAXException e) {
+ throw new IOException(e);
+ }
+ serialize(cas, jch);
+ }
+
+ /**
+ * Serialize a Cas to an Output configured in the passed in JsonContentHandlerJacksonWrapper
+ * Constructs a new CasDocSerializer instance to do the serialization,
+ * configured using this class's Delta marker setting (if any)
+ * @param cas The CAS to serialize
+ * @param jch the configured content handler
+ * @throws IOException if there was an IOException
+ */
+ public void serialize(CAS cas, JsonContentHandlerJacksonWrapper jch) throws IOException {
+ JsonDocSerializer ser = new JsonDocSerializer(jch, ((CASImpl) cas).getBaseCAS());
+ try {
+ ser.cds.needNameSpaces = false;
+ ser.cds.serialize();
+ } catch (Exception e) {
+ throw (IOException) e;
+ }
+ }
+
+ /********************************************************
+ * Routines to set/reset configuration *
+ ********************************************************/
+ /**
+ * set or reset the pretty print flag (default is false)
+ * @param pp true to do pretty printing of output
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setPrettyPrint(boolean pp) {
+ css.setPrettyPrint(pp);
+ return this;
+ }
+
+ /**
+ * set which JsonFactory instance to use; if null, a new instance is used
+ * this can be used to preconfigure the JsonFactory instance
+ * @param jsonFactory -
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setJsonFactory(JsonFactory jsonFactory) {
+ this.jsonFactory = jsonFactory;
+ return this;
+ }
+
+ /**
+ * pass in a type system to use for filtering what gets serialized;
+ * only those types and features which are defined this type system are included.
+ * @param ts the filter
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setFilterTypes(TypeSystemImpl ts) {
+ css.setFilterTypes(ts);
+ return this;
+ }
+
+ /**
+ * set the Marker to specify delta cas serialization
+ * @param m - the marker
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setDeltaCas(Marker m) {
+ css.setDeltaCas(m);
+ return this;
+ }
+
+ /**
+ * set an error handler to receive information about errors
+ * @param eh the error handler
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setErrorHandler(ErrorHandler eh) {
+ css.setErrorHandler(eh);
+ return this;
+ }
+
+ /**
+ * adds the style of Json formatting desired.
+ * @param format specifies the style
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer jsonCasFormatEnable(JsonCasFormat format) {
+ jsonCasFormat.add(format);
+ if (format == JsonCasFormat.INDEX_ID) {
+ jsonCasFormat.remove(JsonCasFormat.INDEX_TYPE);
+ } else if (format == JsonCasFormat.INDEX_TYPE) {
+ jsonCasFormat.remove(JsonCasFormat.INDEX_ID);
+ }
+ return this;
+ }
+
+ /**
+ * removes the style of Json formatting desired.
+ * @param format specifies the style
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer jsonCasFormatDisable(JsonCasFormat format) {
+ jsonCasFormat.remove(format);
+ return this;
+ }
+
+ /**
+ * sets which Json context format to use when serializing
+ * @param format the format to use for the serialization
+ * Specifying the context flag also specifies all 3 subflags
+ * Specifying one of the subflags as true sets the context flag to true if it isn't already
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setJsonContext(JsonContextFormat format) {
+ switch (format) {
+ case omitContext:
+ isWithContext = false;
+ isWithSupertypes = false;
+ isWithFeatureRefs = false;
+ isWithExpandedTypeNames = false; break;
+ case includeContext:
+ isWithContext = true;
+ isWithSupertypes = true;
+ isWithFeatureRefs = true;
+ isWithExpandedTypeNames = true; break;
+
+ case omitSupertypes:
+ isWithSupertypes = false; break;
+ case includeSuperTypes:
+ isWithSupertypes = true;
+ isWithContext = true; break;
+
+
+ case omitFeatureRefs:
+ isWithFeatureRefs = false; break;
+ case includeFeatureRefs:
+ isWithFeatureRefs = true;
+ isWithContext = true; break;
+
+ case omitExpandedTypeNames:
+ isWithExpandedTypeNames = false; break;
+ case includeExpandedTypeNames:
+ isWithExpandedTypeNames = true;
+ isWithContext = true; break;
+ }
+ return this;
+ }
+
+ /**
+ * Causes the index information to be included in the serialization
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setCasViews() {
+ return setCasViews(true);
+ }
+
+ /**
+ * Sets whether or not to include which Feature Structures were indexed, by view
+ * @param includeViews true to include the index information
+ * @return the original instance, possibly updated
+ */
+ public JsonCasSerializer setCasViews(boolean includeViews) {
+ isWithViews = includeViews;
+ return this;
+ }
+
+ public JsonCasSerializer setOmitDefaultValues(boolean omitDefaultValues) {
+ isOmitDefaultValues = omitDefaultValues;
+ return this;
+ }
+
+ class JsonDocSerializer extends CasSerializerSupportSerialize {
+
+ private final CasDocSerializer cds;
+
+ private final JsonContentHandlerJacksonWrapper jch;
+
+ private final JsonGenerator jg;
+
+ private final Set<Type> jsonRecordedSuperTypes = new HashSet<Type>();
+
+ private final Map<String, SerializedString> serializedStrings = new HashMap<String, SerializedString>();
+
+ private final Map<String, XmlElementName> usedTypeName2XmlElementName;
+
+ private int lastEncodedTypeCode;
+
+ private final boolean isOmitDefaultValues;
+
+ private final boolean isWithContext;
+
+ private final boolean isWithExpandedTypeNames;
+
+ private final boolean isWithFeatureRefs;
+
+ private final boolean isWithSupertypes;
+
+ private final boolean isWithViews;
+
+ private final boolean isWithContextOrViews;
+
+ private final boolean omitType;
+ private final boolean omitId;
+ private final boolean indexType;
+ private final boolean indexId;
+
+ private JsonDocSerializer(ContentHandler ch, CASImpl cas) {
+ cds = css.new CasDocSerializer(ch, cas, null, this);
+ this.isOmitDefaultValues = JsonCasSerializer.this.isOmitDefaultValues;
+ boolean tempIsWithContext = JsonCasSerializer.this.isWithContext;
+ isWithExpandedTypeNames = JsonCasSerializer.this.isWithExpandedTypeNames;
+ isWithFeatureRefs = JsonCasSerializer.this.isWithFeatureRefs;
+ isWithSupertypes = JsonCasSerializer.this.isWithSupertypes;
+ isWithViews = JsonCasSerializer.this.isWithViews;
+ jch = (JsonContentHandlerJacksonWrapper) ch;
+ jg = jch.getJsonGenerator();
+ isWithContext = (tempIsWithContext && !isWithSupertypes && !isWithFeatureRefs && !isWithExpandedTypeNames) ?
+ false : tempIsWithContext;
+ isWithContextOrViews = isWithContext || isWithViews;
+ usedTypeName2XmlElementName = new HashMap<String, XmlElementName>(cds.tsi.getNumberOfTypes());
+
+ omitType = JsonCasSerializer.this.jsonCasFormat.contains(JsonCasFormat.OMIT_TYPE);
+ omitId = JsonCasSerializer.this.jsonCasFormat.contains(JsonCasFormat.OMIT_ID);
+ indexId = JsonCasSerializer.this.jsonCasFormat.contains(JsonCasFormat.INDEX_ID);
+ indexType = indexId ? false : JsonCasSerializer.this.jsonCasFormat.contains(JsonCasFormat.INDEX_TYPE);
+ }
+
+ protected void initializeNamespaces() {
+ if (cds.sharedData != null &&
+ (null != cds.sharedData.getOutOfTypeSystemElements() ||
+ cds.sharedData.hasOutOfTypeSystemArrayElements())) {
+ throw new UnsupportedOperationException("Can't do JSON serialization "
+ + "if there are out-of-type-system elements,"
+ + " because there's no type information available (needed for @context)");
+ }
+ }
+
+ protected void writeViews() throws Exception {
+ if (!isWithViews) {
+ return;
+ }
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(JSON_CAS_VIEWS);
+ jg.writeStartObject();
+
+ cds.writeViewsCommons(); // encodes cas.sofaCount + 1 elements
+ jg.writeEndObject(); // and end of views property
+ }
+
+ protected void writeFeatureStructures(int elementCount /* not used */ ) throws Exception{
+ jch.withoutNl(); // set up prettyprint mode so this class controls it
+ if (isWithContextOrViews) {
+ jg.writeStartObject(); // container for context, fss, and views
+ }
+ if (isWithContext) {
+ serializeJsonLdContext();
+ }
+ if (isWithContextOrViews) {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(JSON_CAS_FEATURE_STRUCTURES);
+ }
+
+ if (indexId || indexType) {
+ jg.writeStartObject(); // either outer object, or object of JSON_CAS_FEATURE_STRUCTURES
+ } else {
+ jg.writeStartArray();
+ }
+
+ if (indexType) {
+ Integer[] allFss = cds.collectAllFeatureStructures();
+ Arrays.sort(allFss, cds.sortFssByType);
+ encodeAllFss(allFss);
+ } else {
+ cds.encodeIndexed();
+ cds.encodeQueued();
+ }
+
+ // out of type system data not supported - no type info for @context available
+// if (!cds.isDelta) { // if delta, the out-of-type-system elements, are guaranteed not to be modified
+// serializeOutOfTypeSystemElements(); //encodes sharedData.getOutOfTypeSystemElements().size() elements
+// }
+
+ if (indexId || indexType) {
+ jg.writeEndObject(); // either outer object, or object of JSON_CAS_FEATURE_STRUCTURES
+ } else {
+ jg.writeEndArray();
+ }
+ }
+
+ protected void writeEndOfSerialization() throws IOException {
+ if (isWithContextOrViews) {
+ jg.writeEndObject(); // wrapper of @context and cas
+ }
+ jg.flush();
+ }
+
+
+ protected void writeView(int sofaAddr, int[] members) throws IOException {
+ jch.writeNlJustBeforeNext();
+ String xmiId = (0 == sofaAddr) ? "0" : cds.getXmiId(sofaAddr);
+ jg.writeArrayFieldStart(xmiId);
+ writeViewMembers(members);
+ //check for out-of-typesystem members
+ if (cds.sharedData != null) {
+ String sofaXmiId = cds.getXmiId(sofaAddr);
+ List<String> ootsMembers = cds.sharedData.getOutOfTypeSystemViewMembers(sofaXmiId);
+ jch.writeNlJustBeforeNext();
+ writeViewMembers(ootsMembers);
+ }
+
+ jg.writeEndArray();
+ }
+
+ private void writeViewForDeltas(SerializedString kind, int[] deltaMembers) throws IOException {
+ jg.writeFieldName(kind);
+ jg.writeStartArray();
+ writeViewMembers(deltaMembers);
+ jg.writeEndArray();
+ }
+
+ protected void writeView(int sofaAddr, int[] added, int[] deleted, int[] reindexed) throws IOException {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(Integer.toString(sofaAddr));
+ jg.writeStartObject();
+ writeViewForDeltas(JSON_VIEW_ADDED, added);
+ writeViewForDeltas(JSON_VIEW_DELETED, deleted);
+ writeViewForDeltas(JSON_VIEW_REINDEXED, reindexed);
+ jg.writeEndObject();
+ }
+
+ private void writeViewMembers(int[] members) throws IOException {
+ int nextBreak = CasSerializerSupport.PP_ELEMENTS;
+ int i = 0;
+ for (int member : members) {
+ int xmiId = cds.getXmiIdAsInt(member);
+ if (xmiId == 0) {
+ continue;
+ }
+ if (i++ > nextBreak) {
+ jch.writeNlJustBeforeNext();
+ nextBreak += CasSerializerSupport.PP_ELEMENTS;
+ }
+ jg.writeNumber(xmiId);
+ }
+ }
+
+ /**
+ * version for oots data
+ */
+
+ private void writeViewMembers(List<String> members) throws IOException {
+ int nextBreak = CasSerializerSupport.PP_ELEMENTS;
+ int i = 0;
+ for (String xmiId : members) {
+ if (null == xmiId || xmiId.length() == 0) {
+ continue;
+ }
+ if (i++ > nextBreak) {
+ jch.writeNlJustBeforeNext();
+ nextBreak += CasSerializerSupport.PP_ELEMENTS;
+ }
+ jg.writeNumber(Integer.parseInt(xmiId));
+ }
+ }
+ /**
+ * JSON: serialize context info
+ *
+ * (for namespaces - may be omitted if not needed for disambiguation)
+ * "nameSpacePrefix" : IRI-for-namespace
+ *
+ * (for each used type)
+ * "typeName - or nameSpace:typeName" : {
+ * "@id" : expanded-name-as-IRI,
+ * "@superTypes" : [xxx, yyy, zzz, uima.cas.TOP],
+ * "@featureRefs" : [ "featName1", "featName2"],
+ * "@featureRefsIfSingle" : [ "featName1", ...] }
+ *
+ * @featureRefs: the named feature values are a number or an array of numbers, all of which
+ * are to be interpreted as an ID of another serialized Feature Structure (unless 0,
+ * which is like a null reference)
+ * @featureRefsIfSingle: the named feature values, if a single number, are to be interpreted
+ * as an ID of another serialized feature (unless 0).
+ * If the value is an array, then the array of values is the value of that slot,
+ * not (if numbers) references to other Feature Structures.
+ *
+ * superType values are longNames
+ * if no featureRefs, omit
+ * @throws IOException
+ */
+
+ private void serializeJsonLdContext() throws IOException {
+ // @context : { n : v ... }}
+ jg.writeFieldName(JSON_CONTEXT_TAG_LOCAL_NAME);
+// jch.setDoNL();
+ jg.writeStartObject();
+
+// if (needNameSpaces) {
+// serializeNameSpaceInfo();
+// }
+
+ for (TypeImpl ti : cds.getSortedUsedTypes()) {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(getSerializedTypeName(ti.getCode()));
+ jg.writeStartObject();
+ jch.writeNlJustBeforeNext();
+ if (isWithExpandedTypeNames) {
+ jg.writeFieldName(JSON_ID_ATTR_NAME); // form for using SerializedString
+ jg.writeString(ti.getName());
+ }
+ if (isWithFeatureRefs) {
+ addJsonFeatRefs(ti);
+ }
+ if (isWithSupertypes) {
+ addJsonSuperTypes(ti);
+ }
+ jg.writeEndObject(); // end of one type
+ }
+ jg.writeEndObject(); // end of context
+ }
+
+ /**
+ * Adds the ", @featureRefs" : [ "featName1", "featName2"]
+ * for features that are being serialized as references.
+ *
+ * @param type the range type of the Feature
+ * @throws IOException
+ */
+ private void addJsonFeatRefs(TypeImpl type) throws IOException {
+ final int typeCode = type.getCode();
+ final int[] feats = cds.tsi.ll_getAppropriateFeatures(typeCode);
+ boolean started = false;
+ for (int featCode : feats) {
+ final int fsClass = cds.classifyType(cds.tsi.range(featCode));
+
+ if (isRefToFS(fsClass, featCode)) {
+ if (!started) {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(FEATURE_REFS_NAME);
+ jg.writeStartArray();
+ started = true;
+ }
+ jg.writeString(getSerializedString(cds.tsi.ll_getFeatureForCode(featCode).getShortName()));
+ }
+ }
+ if (started) {
+ jg.writeEndArray();
+ }
+ }
+
+ /**
+ * Add superType chain to required type, up to point already covered
+ * @throws IOException
+ */
+ private void addJsonSuperTypes(TypeImpl ti) throws IOException {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(SUPER_TYPES_NAME);
+ jg.writeStartArray();
+ for (TypeImpl parent = (TypeImpl) ti.getSuperType();
+ parent != null;
+ parent = (TypeImpl) parent.getSuperType()) {
+ jg.writeString(parent.getName());
+ if (!jsonRecordedSuperTypes.add(parent)) {
+ break; // if this item already output
+ }
+ }
+ jg.writeEndArray();
+ }
+
+ private void encodeAllFss(Integer[] allFss) throws IOException {
+ lastEncodedTypeCode = -1;
+ try {
+ for (Integer i : allFss) {
+ cds.encodeFS(i);
+ }
+ } catch (Exception e) {
+ throw (IOException) e;
+ }
+ if (lastEncodedTypeCode != -1) {
+ jg.writeEndArray();
+ }
+ }
+
+ private SerializedString getSerializedTypeName(int typeCode) {
+ XmlElementName xe = cds.typeCode2namespaceNames[typeCode];
+ return getSerializedString(cds.needNameSpaces ? xe.qName : xe.localName);
+ }
+
+ private SerializedString getSerializedString(String s) {
+ SerializedString ss = serializedStrings.get(s);
+ if (ss == null) {
+ ss = new SerializedString(s);
+ serializedStrings.put(s, ss);
+ }
+ return ss;
+ }
+
+ protected void enqueueIncoming() {}
+
+ protected void enqueueNonsharedMultivaluedFS() {}
+
+ protected boolean checkForNameCollision(XmlElementName xmlElementName) {
+ XmlElementName xel = usedTypeName2XmlElementName.get(xmlElementName.localName);
+ if (xel != null) {
+ if (xel.nsUri.equals(xmlElementName.nsUri)) {
+ return false; // don't need name spaces yet
+ } else {
+ usedTypeName2XmlElementName.clear(); // not needed anymore
+ return true; // collision - need name space
+ }
+ }
+ usedTypeName2XmlElementName.put(xmlElementName.localName, xmlElementName);
+ return false;
+ }
+
+ protected void writeFsStart(int addr, int typeCode) throws IOException {
+
+ if (indexId) {
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(cds.getXmiId(addr));
+ jg.writeStartObject(); // start of feat : value
+ } else if (indexType) { // fs's as arrays under typeName
+ if (typeCode != lastEncodedTypeCode) {
+ if (lastEncodedTypeCode != -1) {
+ // close off previous Array
+ jg.writeEndArray();
+ }
+ lastEncodedTypeCode = typeCode;
+ jch.writeNlJustBeforeNext();
+ jg.writeFieldName(getSerializedTypeName(typeCode));
+ jg.writeStartArray();
+ }
+ jch.writeNlJustBeforeNext();
+ jg.writeStartObject(); // start of feat : value
+ } else { // no index
+ jch.writeNlJustBeforeNext();
+ jg.writeStartObject();
+ }
+ }
+
+ private void maybeWriteIdFeat(int addr) throws IOException {
+ if (!omitId) {
+ jg.writeFieldName(JSON_ID_ATTR_NAME);
+ jg.writeNumber(cds.getXmiIdAsInt(addr));
+ }
+ }
+
+ private void maybeWriteTypeFeat(int typeCode) throws IOException {
+ if (!omitType) {
+ jg.writeFieldName(JSON_TYPE_ATTR_NAME);
+ jg.writeString(getSerializedTypeName(typeCode));
+ }
+ }
+
+ protected void writeFs(int addr, int typeCode) throws IOException {
+ writeFsOrLists(addr, typeCode, false);
+ }
+
+ protected void writeListsAsIndividualFSs(int addr, int typeCode) throws IOException {
+ writeFsOrLists(addr, typeCode, true);
+ }
+
+ private void writeFsOrLists(int addr, int typeCode, boolean isListAsFSs) throws IOException {
+ final int[] feats = cds.tsi.ll_getAppropriateFeatures(typeCode);
+
+ maybeWriteIdFeat(addr);
+ maybeWriteTypeFeat(typeCode);
+
+ for (final int featCode : feats) {
+
+ if (cds.isFiltering) {
+ // skip features that aren't in the target type system
+ String fullFeatName = cds.tsi.ll_getFeatureForCode(featCode).getName();
+ if (cds.filterTypeSystem.getFeatureByFullName(fullFeatName) == null) {
+ continue;
+ }
+ }
+
+ final String featName = cds.tsi.ll_getFeatureForCode(featCode).getShortName();
+ final int featAddr = addr + cds.cas.getFeatureOffset(featCode);
+ final int featValRaw = cds.cas.getHeapValue(featAddr);
+ final int featureClass = cds.classifyType(cds.tsi.range(featCode));
+
+// jg.writeFieldName(getSerializedString(featName)); // not done here, because if null feat can be omitted
+
+ switch (featureClass) {
+
+ case LowLevelCAS.TYPE_CLASS_BYTE:
+ case LowLevelCAS.TYPE_CLASS_SHORT:
+ case LowLevelCAS.TYPE_CLASS_INT:
+ if (featValRaw == 0 && isOmitDefaultValues) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeNumber(featValRaw);
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_FS:
+ if (featValRaw == 0/* && isOmitDefaultValues*/) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeNumber(cds.getXmiIdAsInt(featValRaw));
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_LONG:
+ final long longVal = cds.cas.ll_getLongValue(featValRaw);
+ if (longVal == 0L && isOmitDefaultValues) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeNumber(longVal);
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_FLOAT:
+ final float floatVal = CASImpl.int2float(featValRaw);
+ if (floatVal == 0.F && isOmitDefaultValues) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeNumber(floatVal);
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_DOUBLE:
+ final double doubleVal = cds.cas.ll_getDoubleValue(addr, featCode);
+ if (doubleVal == 0L && isOmitDefaultValues) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeNumber(doubleVal);
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_BOOLEAN:
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeBoolean(cds.cas.ll_getBooleanValue(addr, featCode));
+ break;
+
+ case LowLevelCAS.TYPE_CLASS_STRING:
+ if (featValRaw == 0 /*&& isOmitDefaultValues*/) continue;
+ jg.writeFieldName(getSerializedString(featName));
+ jg.writeString(cds.cas.getStringForCode(featValRaw));
+ break;
+
+ // all other fields (arrays, lists, fsRefs) can be null and are omitted if so
+ default:
+ if (featValRaw != CASImpl.NULL /*|| !isOmitDefaultValues*/) {
+
+ jg.writeFieldName(getSerializedString(featName));
+
+ if (featureClass == LowLevelCAS.TYPE_CLASS_INTARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_FLOATARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_BOOLEANARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_BYTEARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_SHORTARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_LONGARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_DOUBLEARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_STRINGARRAY ||
+ featureClass == LowLevelCAS.TYPE_CLASS_FSARRAY) {
+
+ if (cds.tsi.ll_getFeatureForCode(featCode).isMultipleReferencesAllowed()) {
+ jg.writeNumber(cds.getXmiIdAsInt(featValRaw));
+ } else {
+ writeJsonArrayValues(featValRaw, featureClass);
+ }
+
+ } else if (featureClass == CasSerializerSupport.TYPE_CLASS_INTLIST ||
+ featureClass == CasSerializerSupport.TYPE_CLASS_FLOATLIST ||
+ featureClass == CasSerializerSupport.TYPE_CLASS_STRINGLIST ||
+ featureClass == CasSerializerSupport.TYPE_CLASS_FSLIST) {
+
+ if (isListAsFSs || cds.tsi.ll_getFeatureForCode(featCode).isMultipleReferencesAllowed()) {
+ jg.writeNumber(cds.getXmiIdAsInt(featValRaw));
+ } else {
+ writeJsonListValues(featValRaw);
+ }
+ } else { // is error
+ throw new RuntimeException("Invalid State, featureClass was "+ featureClass);
+ }
+ } // end of default case with non-null values
+ } // end of switch
+ } // end of loop over all features
+ }
+
+ protected void writeArrays(int addr, int typeCode, int typeClass) throws IOException {
+ maybeWriteIdFeat(addr);
+ maybeWriteTypeFeat(typeCode);
+
+ jg.writeFieldName(JSON_COLLECTION_ATTR_NAME);
+ writeJsonArrayValues(addr, typeClass);
+ }
+
+ protected void writeEndOfIndividualFs() throws IOException {
+ jg.writeEndObject();
+ }
+
+ // writes a set of values in a JSON array
+ // or null if the reference to the UIMA array is actually null
+ // 0 length arrays are written as []
+ private void writeJsonArrayValues(int addr, int arrayType) throws IOException {
+ if (addr == CASImpl.NULL) {
+ jg.writeNull();
+ return;
+ }
+
+ final int size = cds.cas.ll_getArraySize(addr);
+
+ jg.writeStartArray();
+ int pos = cds.cas.getArrayStartAddress(addr);
+
+ if (arrayType == LowLevelCAS.TYPE_CLASS_FSARRAY) {
+
+ List<XmiArrayElement> ootsArrayElementsList = cds.sharedData == null ? null :
+ cds.sharedData.getOutOfTypeSystemArrayElements(addr);
+ int ootsIndex = 0;
+
+ for (int j = 0; j < size; j++) { // j used to id the oots things
+ int heapValue = cds.cas.getHeapValue(pos++);
+
+ if (heapValue == CASImpl.NULL) {
+ // this null array element might have been a reference to an
+ // out-of-typesystem FS, which, when deserialized, was replaced with NULL,
+ // so check the ootsArrayElementsList
+ boolean found = false;
+ if (ootsArrayElementsList != null) {
+
+ while (ootsIndex < ootsArrayElementsList.size()) {
+ XmiArrayElement arel = ootsArrayElementsList.get(ootsIndex++);
+ if (arel.index == j) {
+ jg.writeNumber(Integer.parseInt(arel.xmiId));
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ jg.writeNumber(0);
+ }
+
+ // else, not null FS ref
+ } else {
+ if (cds.isFiltering) { // return as null any references to types not in target TS
+ String typeName = cds.tsi.ll_getTypeForCode(cds.cas.getHeapValue(addr)).getName();
+ if (cds.filterTypeSystem.getType(typeName) == null) {
+ heapValue = CASImpl.NULL;
+ }
+ }
+ jg.writeNumber(cds.getXmiIdAsInt(heapValue));
+ }
+ } // end of loop over all refs in FS array
+
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_BYTEARRAY) {
+ // special case for byte arrays:
+ // serialize using standard JACKSON/JSON binary serialization
+ // lazy - doing extra copy to avoid figuring out the impl details
+ ByteArrayFS byteArrayFS = new ByteArrayFSImpl(addr, cds.cas);
+ int length = byteArrayFS.size();
+ byte[] byteArray = new byte[length];
+ byteArrayFS.copyToArray(0, byteArray, 0, length);
+
+ jg.writeBinary(byteArray);
+
+ } else {
+ for (int i = 0; i < size; i++) {
+ if (arrayType == LowLevelCAS.TYPE_CLASS_BOOLEANARRAY) {
+ jg.writeBoolean(cds.cas.ll_getBooleanArrayValue(addr, i));
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_STRINGARRAY) {
+ jg.writeString(cds.cas.ll_getStringArrayValue(addr, i));
+// } else if (arrayType == LowLevelCAS.TYPE_CLASS_BYTEARRAY) {
+// jg.writeNumber(cds.cas.ll_getByteArrayValue(addr, i));
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_SHORTARRAY) {
+ jg.writeNumber(cds.cas.ll_getShortArrayValue(addr, i));
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_INTARRAY) {
+ jg.writeNumber(cds.cas.ll_getIntArrayValue(addr, i));
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_LONGARRAY) {
+ jg.writeNumber(cds.cas.ll_getLongArrayValue(addr, i));
+ } else if (arrayType == LowLevelCAS.TYPE_CLASS_FLOATARRAY) {
+ jg.writeNumber(cds.cas.ll_getFloatArrayValue(addr, i));
+ } else {
+ jg.writeNumber(cds.cas.ll_getDoubleArrayValue(addr, i));
+ }
+ }
+ }
+ jg.writeEndArray();
+ }
+
+ // a null ref is written as null
+ // an empty list is written as []
+ /**
+ * Only called if no sharing of list nodes exists.
+ * Only called for list nodes referred to by Feature value slots in some non-list FS.
+
+ * @param curNode the address of the start of the list
+ * @throws IOException
+ */
+ private void writeJsonListValues(int curNode) throws IOException {
+ if (curNode == CASImpl.NULL) {
+// jg.writeNull();
+// return;
+ throw new RuntimeException("never happen");
+ }
+ final ListUtils listUtils = cds.listUtils;
+ final int startNodeType = cds.cas.getHeapValue(curNode);
+
+ int headFeat = listUtils.getHeadFeatCode(startNodeType);
+ int tailFeat = listUtils.getTailFeatCode(startNodeType);
+ int neListType = listUtils.getNeListType(startNodeType); // non-empty
+
+ jg.writeStartArray();
+ while (curNode != CASImpl.NULL) {
+
+ final int curNodeType = cds.cas.getHeapValue(curNode);
+ if (curNodeType != neListType) { // if not "non-empty"
+ break; // would be the end element. a 0 is also treated as an end element
+ }
+
+ final int val = cds.cas.getHeapValue(curNode + cds.cas.getFeatureOffset(headFeat));
+
+ if (curNodeType == listUtils.neStringListType) {
+ jg.writeString(cds.cas.getStringForCode(val));
+ } else if (curNodeType == listUtils.neFloatListType) {
+ jg.writeNumber(CASImpl.int2float(val));
+ } else if (curNodeType == listUtils.neFsListType) {
+ jg.writeNumber(cds.getXmiIdAsInt(val));
+ } else { // for ints
+ jg.writeNumber(val);
+ }
+
+ curNode = cds.cas.getHeapValue(curNode + cds.cas.getFeatureOffset(tailFeat));
+ }
+ jg.writeEndArray();
+
+ }
+
+ /**
+ * Return true if the range of the feature is a fsRef, or a
+ * collection of fsRefs (array or list), where the feature is marked
+ * multiple references allowed.
+ *
+ * Return false for other primitives, or collections (arrays or lists) of primitives
+ * where the collection (feature) is marked as multipleReferencesAllowed = false
+ *
+ *
+ * @param fsClass the class of the feature
+ * @param featCode the feature code
+ * @return true if the serialization is being done by having the value of this feature be a reference id
+ * or (in the case of embedded collections, where the collection items are feature references
+ */
+
+ private boolean isRefToFS(int fsClass, int featCode) {
+ switch (fsClass) {
+ case LowLevelCAS.TYPE_CLASS_FS:
+ case LowLevelCAS.TYPE_CLASS_FSARRAY:
+ case CasSerializerSupport.TYPE_CLASS_FSLIST:
+ return true;
+
+ case LowLevelCAS.TYPE_CLASS_INTARRAY:
+ case LowLevelCAS.TYPE_CLASS_FLOATARRAY:
+ case LowLevelCAS.TYPE_CLASS_STRINGARRAY:
+ case LowLevelCAS.TYPE_CLASS_BOOLEANARRAY:
+ case LowLevelCAS.TYPE_CLASS_BYTEARRAY:
+ case LowLevelCAS.TYPE_CLASS_SHORTARRAY:
+ case LowLevelCAS.TYPE_CLASS_LONGARRAY:
+ case LowLevelCAS.TYPE_CLASS_DOUBLEARRAY:
+ case CasSerializerSupport.TYPE_CLASS_INTLIST:
+ case CasSerializerSupport.TYPE_CLASS_FLOATLIST:
+ case CasSerializerSupport.TYPE_CLASS_STRINGLIST:
+ // we have refs only if the feature has
+ // multipleReferencesAllowed = true
+ return cds.tsi.ll_getFeatureForCode(featCode).isMultipleReferencesAllowed();
+
+ default: // for primitives
+ return false;
+ }
+ }
+
+ /**
+ * Converts a UIMA-style dotted type name to the element name that should be used in the XMI
+ * serialization. The XMI element name consists of three parts - the Namespace URI, the Local
+ * Name, and the QName (qualified name).
+ *
+ * @param uimaTypeName
+ * a UIMA-style dotted type name
+ * @return a data structure holding the three components of the XML element name
+ */
+ protected XmlElementName uimaTypeName2XmiElementName(String uimaTypeName) {
+ // split uima type name into namespace and short name
+ String shortName;
+ final int lastDotIndex = uimaTypeName.lastIndexOf('.');
+ if (lastDotIndex == -1) { // no namespace
+ shortName = uimaTypeName;
+ } else {
+ shortName = uimaTypeName.substring(lastDotIndex + 1);
+ }
+ // convert short name to shared string, without interning, reduce GCs
+ shortName = cds.getUniqueString(shortName);
+
+ // determine what namespace prefix to use
+ String prefix = cds.getNameSpacePrefix(uimaTypeName, uimaTypeName, lastDotIndex);
+
+ return new XmlElementName(uimaTypeName, shortName, cds.getUniqueString(prefix + ':' + shortName));
+ }
+
+ }
+
+}
Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/ListUtils.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/ListUtils.java?rev=1623582&r1=1623581&r2=1623582&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/ListUtils.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/ListUtils.java Mon Sep 8 21:12:55 2014
@@ -19,15 +19,17 @@
package org.apache.uima.cas.impl;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.impl.XmiSerializationSharedData.OotsElementData;
+import org.apache.uima.internal.util.IntHashSet;
import org.apache.uima.internal.util.IntVector;
+import org.apache.uima.internal.util.PositiveIntSet;
import org.apache.uima.internal.util.XmlAttribute;
-import org.apache.uima.internal.util.rb_trees.IntRedBlackTree;
import org.apache.uima.util.Level;
import org.apache.uima.util.Logger;
import org.xml.sax.ErrorHandler;
@@ -39,6 +41,8 @@ import org.xml.sax.SAXParseException;
*
*/
public class ListUtils {
+ private static final List<String> EMPTY_LIST_STRING = Collections.emptyList();
+
CASImpl cas;
// list type and feature codes
@@ -79,7 +83,7 @@ public class ListUtils {
private int stringTailFeat;
- private int fsHeadFeat;
+ int fsHeadFeat;
private int fsTailFeat;
@@ -152,6 +156,16 @@ public class ListUtils {
(isFsListType(type)) ? neFsListType :
-1;
}
+
+ public int getEListType(int type) {
+ return
+ (isIntListType(type)) ? eIntListType :
+ (isFloatListType(type)) ? eFloatListType :
+ (isStringListType(type)) ? eStringListType :
+ (isFsListType(type)) ? eFsListType :
+ -1;
+ }
+
public boolean isIntListType(int type) {
return (type == this.intListType || type == this.neIntListType || type == this.eIntListType);
@@ -181,73 +195,105 @@ public class ListUtils {
}
public int getLength(int type, int addr, int neListType, int tailFeat) {
- IntRedBlackTree visited = new IntRedBlackTree();
- foundCycle = false;
- // first count length of list so we can allocate array
- int length = 0;
- int curNode = addr;
- while (cas.getHeapValue(curNode) == neListType) {
- if (!visited.put(curNode, curNode)) {
- foundCycle = true;
- break;
- }
- length++;
- curNode = cas.getHeapValue(curNode + cas.getFeatureOffset(tailFeat));
- }
- return length;
+ PositiveIntSet visited = new PositiveIntSet();
+ foundCycle = false;
+ // first count length of list so we can allocate array
+ int length = 0;
+ int curNode = addr;
+ while (cas.getHeapValue(curNode) == neListType) {
+ if (!visited.add(curNode)) {
+ foundCycle = true;
+ break;
+ }
+ length++;
+ curNode = cas.getHeapValue(curNode + cas.getFeatureOffset(tailFeat));
+ }
+ return length;
}
-
- public String[] anyListToStringArray(int curNode, XmiSerializationSharedData sharedData) throws SAXException {
- final int type = cas.getHeapValue(curNode);
- int headFeat = getHeadFeatCode(type);
- int tailFeat = getTailFeatCode(type);
- int neListType = getNeListType(type);
- int length = getLength(type, curNode, neListType, tailFeat);
- String[] array = new String[length];
+ static abstract class ListOutput {
+ abstract void append(String item);
+ }
+
+ public void anyListToOutput(
+ int curNode,
+ XmiSerializationSharedData sharedData,
+ CasSerializerSupport.CasDocSerializer cds,
+ ListOutput out) {
+ if (curNode == CASImpl.NULL) {
+ return;
+ }
- // now populate list
-// curNode = addr;
- for (int i = 0; i < length; i++) {
+ final int type = cas.getHeapValue(curNode);
+ final int headFeat = getHeadFeatCode(type);
+ final int tailFeat = getTailFeatCode(type);
+ final int neListType = getNeListType(type);
+
+ while (curNode != CASImpl.NULL) {
+ final int curNodeType = cas.getHeapValue(curNode);
+ if (curNodeType != neListType) { // if not "non-empty"
+ break; // would be the end element. a 0 is also treated as an end element
+ }
+
final int val = cas.getHeapValue(curNode + cas.getFeatureOffset(headFeat));
- if (neListType == neIntListType) {
- array[i] = Integer.toString(val);
- } else if (neListType == neFloatListType) {
- array[i] = Float.toString(val);
- } else if (neListType == neStringListType) {
- array[i] = cas.getStringForCode(val);
- } else if (neListType == neFsListType) {
+ if (curNodeType == neStringListType) {
+ out.append(cas.getStringForCode(val));
+ }
+ if (curNodeType == neIntListType) {
+ out.append(Integer.toString(val));
+
+ } else if (curNodeType == neFloatListType) {
+ out.append(Float.toString(CASImpl.int2float(val)));
+
+ } else if (curNodeType == neFsListType) {
if (val == 0) {
- //null value in list. Represent with "0".
- array[i] = "0";
- // However, this may be null because the element was originally a reference to an
- // out-of-typesystem FS, so chck the XmiSerializationSharedData
if (sharedData != null) {
OotsElementData oed = sharedData.getOutOfTypeSystemFeatures(curNode);
if (oed != null) {
assert oed.attributes.size() == 1; //only the head feature can possibly be here
XmlAttribute attr = (XmlAttribute)oed.attributes.get(0);
assert CAS.FEATURE_BASE_NAME_HEAD.equals(attr.name);
- array[i] = attr.value;
+ out.append(attr.value);
+ } else {
+ out.append("0");
}
- }
- }
- else {
- if (sharedData != null) {
- array[i] = (val == 0) ? "0" : sharedData.getXmiId(val); // changed from null 8/2014 mis
} else {
- array[i] = Integer.toString(val);
+ out.append("0");
}
+ } else if (sharedData != null) {
+ out.append(sharedData.getXmiId(val));
+ } else {
+ out.append(Integer.toString(val));
}
- }
+ } // end of Fs List type
+
curNode = cas.getHeapValue(curNode + cas.getFeatureOffset(tailFeat));
- }
- if (foundCycle) {
- reportWarning("Found a cycle in a UIMA List; list truncated to "
- + Arrays.asList(array).toString());
- }
- return array;
+ } // end of while loop
+ }
+
+ public String[] anyListToStringArray(
+ int curNode,
+ XmiSerializationSharedData sharedData) throws SAXException {
+ List<String> r = anyListToStringList(curNode, sharedData, null);
+ return r.toArray(new String[r.size()]);
+ }
+
+ public List<String> anyListToStringList(
+ int curNode,
+ XmiSerializationSharedData sharedData,
+ CasSerializerSupport.CasDocSerializer cds) {
+ if (curNode == CASImpl.NULL) {
+ return EMPTY_LIST_STRING;
+ }
+ final List<String> list = new ArrayList<String>();
+ anyListToOutput(curNode, sharedData, cds, new ListOutput() {
+ @Override
+ void append(String item) {
+ list.add(item);
+ }
+ });
+ return list;
}
//called for enquing
@@ -327,7 +373,7 @@ public class ListUtils {
int currLength = this.getLength(this.neIntListType, addr);
int curNode = addr;
int prevNode = 0;
- IntRedBlackTree visited = new IntRedBlackTree();
+ PositiveIntSet visited = new PositiveIntSet();
boolean foundCycle = false;
int i =0;
@@ -336,7 +382,7 @@ public class ListUtils {
if (currLength < stringValues.size()) {
while (cas.getHeapValue(curNode) == neIntListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -360,7 +406,7 @@ public class ListUtils {
}
} else if (currLength > stringValues.size()) {
while (cas.getHeapValue(curNode) == neIntListType && i < stringValues.size()) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -371,7 +417,7 @@ public class ListUtils {
int finalNode = curNode;
//loop till we get a FS that is of type eStringListType
while (cas.getHeapValue(curNode) == neIntListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
//TODO throw exc
@@ -382,7 +428,7 @@ public class ListUtils {
cas.setFeatureValue(finalNode, intTailFeat, curNode);
} else {
while (cas.getHeapValue(curNode) == neIntListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -403,7 +449,7 @@ public class ListUtils {
int currLength = this.getLength(this.neFloatListType, addr);
int curNode = addr;
int prevNode = 0;
- IntRedBlackTree visited = new IntRedBlackTree();
+ PositiveIntSet visited = new PositiveIntSet();
boolean foundCycle = false;
int i =0;
@@ -411,7 +457,7 @@ public class ListUtils {
// first = createFloatList(stringValues);
if (currLength < stringValues.size()) {
while (cas.getHeapValue(curNode) == neFloatListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -435,7 +481,7 @@ public class ListUtils {
}
} else if (currLength > stringValues.size()) {
while (cas.getHeapValue(curNode) == neFloatListType && i < stringValues.size()) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -446,7 +492,7 @@ public class ListUtils {
int finalNode = curNode;
//loop till we get a FS that is of type eStringListType
while (cas.getHeapValue(curNode) == neFloatListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
//TODO throw exc
@@ -457,7 +503,7 @@ public class ListUtils {
cas.setFeatureValue(finalNode, floatTailFeat, curNode);
} else {
while (cas.getHeapValue(curNode) == neFloatListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -478,7 +524,7 @@ public class ListUtils {
int first = addr;
int currLength = this.getLength(this.neFsListType, addr);
boolean foundCycle = false;
- IntRedBlackTree visited = new IntRedBlackTree();
+ PositiveIntSet visited = new PositiveIntSet();
int curNode = addr;
int prevNode = 0;
@@ -487,7 +533,7 @@ public class ListUtils {
int i=0;
if (currLength < stringValues.size() ) {
while (cas.getHeapValue(curNode) == neFsListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -513,7 +559,7 @@ public class ListUtils {
}
} else if (currLength > stringValues.size()) {
while (cas.getHeapValue(curNode) == neFsListType && i < stringValues.size()) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -525,7 +571,7 @@ public class ListUtils {
int finalNode = curNode;
//loop till we get a FS that is of type eStringListType
while (cas.getHeapValue(curNode) == neFsListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
//TODO throw exc
@@ -536,7 +582,7 @@ public class ListUtils {
cas.setFeatureValue(finalNode, fsTailFeat, curNode);
} else {
while (cas.getHeapValue(curNode) == neFsListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -556,7 +602,7 @@ public class ListUtils {
public int updateStringList(int addr, List<String> stringValues) throws SAXException {
int first = addr;
boolean foundCycle = false;
- IntRedBlackTree visited = new IntRedBlackTree();
+ PositiveIntSet visited = new PositiveIntSet();
int curNode = addr;
int prevNode = 0;
int currLength = this.getLength(this.neStringListType, addr);
@@ -564,7 +610,7 @@ public class ListUtils {
if (currLength < stringValues.size() ) {
int i =0;
while (cas.getHeapValue(curNode) == neStringListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -592,7 +638,7 @@ public class ListUtils {
} else if (currLength > stringValues.size()) {
int i=0;
while (cas.getHeapValue(curNode) == neStringListType && i < stringValues.size()) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
@@ -606,7 +652,7 @@ public class ListUtils {
}
int finalNode = curNode;
while (cas.getHeapValue(curNode) == neStringListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
//TODO throw exc
@@ -617,7 +663,7 @@ public class ListUtils {
} else {
int i =0;
while (cas.getHeapValue(curNode) == neStringListType) {
- if (!visited.put(curNode, curNode)) {
+ if (!visited.add(curNode)) {
foundCycle = true;
break;
}
Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java?rev=1623582&r1=1623581&r2=1623582&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java Mon Sep 8 21:12:55 2014
@@ -922,10 +922,10 @@ public class XmiCasDeserializer {
}
// For list types, we do the same as for array types UNLESS we're dealing with
// the tail feature of another list node. In that case we do the usual FS deserialization.
- case XmiCasSerializer.TYPE_CLASS_INTLIST:
- case XmiCasSerializer.TYPE_CLASS_FLOATLIST:
- case XmiCasSerializer.TYPE_CLASS_STRINGLIST:
- case XmiCasSerializer.TYPE_CLASS_FSLIST: {
+ case CasSerializerSupport.TYPE_CLASS_INTLIST:
+ case CasSerializerSupport.TYPE_CLASS_FLOATLIST:
+ case CasSerializerSupport.TYPE_CLASS_STRINGLIST:
+ case CasSerializerSupport.TYPE_CLASS_FSLIST: {
if (ts.ll_getFeatureForCode(featCode).isMultipleReferencesAllowed()) {
// do the usual FS deserialization
try {
@@ -1026,7 +1026,7 @@ public class XmiCasDeserializer {
break;
}
*/
- case XmiCasSerializer.TYPE_CLASS_INTLIST: {
+ case CasSerializerSupport.TYPE_CLASS_INTLIST: {
int listFS = casBeingFilled.getFeatureValue(addr, featCode);
if (listFS == 0) {
listFS = listUtils.createIntList(featVals);
@@ -1040,7 +1040,7 @@ public class XmiCasDeserializer {
}
break;
}
- case XmiCasSerializer.TYPE_CLASS_FLOATLIST: {
+ case CasSerializerSupport.TYPE_CLASS_FLOATLIST: {
int listFS = casBeingFilled.getFeatureValue(addr, featCode);
if (listFS == 0) {
listFS = listUtils.createFloatList(featVals);
@@ -1054,7 +1054,7 @@ public class XmiCasDeserializer {
}
break;
}
- case XmiCasSerializer.TYPE_CLASS_STRINGLIST: {
+ case CasSerializerSupport.TYPE_CLASS_STRINGLIST: {
int listFS = casBeingFilled.getFeatureValue(addr, featCode);
if (listFS == 0) {
listFS = listUtils.createStringList(featVals);
@@ -1068,7 +1068,7 @@ public class XmiCasDeserializer {
}
break;
}
- case XmiCasSerializer.TYPE_CLASS_FSLIST: {
+ case CasSerializerSupport.TYPE_CLASS_FSLIST: {
// this call, in addition to creating the list in the CAS, also
// adds each list node ID to the fsListNodesFromMultivaluedProperties list.
// We need this so we can go back through later and reset the addresses of the
@@ -1117,23 +1117,23 @@ public class XmiCasDeserializer {
private int createArray(int arrayType, List<String> values, int xmiId, int addr) {
int casArray = -1;
if (addr > 0) { //non-shared preexisting
- if (values.size() == casBeingFilled.getLowLevelCAS().ll_getArraySize(addr)) {
- casArray = addr;
- updateExistingArray(arrayType, values, casArray);
- } else {
- casArray = createNewArray(arrayType, values);
- }
+ if (values.size() == casBeingFilled.getLowLevelCAS().ll_getArraySize(addr)) {
+ casArray = addr;
+ updateExistingArray(arrayType, values, casArray);
+ } else {
+ casArray = createNewArray(arrayType, values);
+ }
} else if (xmiId == -1) { //non-shared new
casArray = createNewArray(arrayType, values);
} else if (isNewFS(xmiId)) { //shared new
- casArray = createNewArray(arrayType,values);
+ casArray = createNewArray(arrayType, values);
} else { //shared preexisting
casArray = getFsAddrForXmiId(xmiId);
if (values.size() == casBeingFilled.getLowLevelCAS().ll_getArraySize(casArray)) {
updateExistingArray(arrayType, values, casArray);
- } else {
+ } else {
casArray = createNewArray(arrayType, values);
- }
+ }
}
deserializedFsAddrs.add(casArray);
@@ -1607,16 +1607,16 @@ public class XmiCasDeserializer {
private final int classifyType(int type) {
// For most most types
if (listUtils.isIntListType(type)) {
- return XmiCasSerializer.TYPE_CLASS_INTLIST;
+ return CasSerializerSupport.TYPE_CLASS_INTLIST;
}
if (listUtils.isFloatListType(type)) {
- return XmiCasSerializer.TYPE_CLASS_FLOATLIST;
+ return CasSerializerSupport.TYPE_CLASS_FLOATLIST;
}
if (listUtils.isStringListType(type)) {
- return XmiCasSerializer.TYPE_CLASS_STRINGLIST;
+ return CasSerializerSupport.TYPE_CLASS_STRINGLIST;
}
if (listUtils.isFsListType(type)) {
- return XmiCasSerializer.TYPE_CLASS_FSLIST;
+ return CasSerializerSupport.TYPE_CLASS_FSLIST;
}
return casBeingFilled.ll_getTypeClass(type);
}