/* | |
* 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.cas; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.IdentityHashMap; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.function.Consumer; | |
import org.apache.uima.cas.CAS; | |
import org.apache.uima.cas.CASRuntimeException; | |
import org.apache.uima.cas.FeatureStructure; | |
import org.apache.uima.cas.impl.CasSerializerSupport; | |
import org.apache.uima.cas.impl.FeatureStructureImplC; | |
import org.apache.uima.cas.impl.XmiSerializationSharedData; | |
import org.apache.uima.cas.impl.XmiSerializationSharedData.OotsElementData; | |
import org.apache.uima.internal.util.XmlAttribute; | |
import org.apache.uima.internal.util.function.Consumer_withSaxException; | |
import org.xml.sax.SAXException; | |
/** | |
* This class is the super class of list nodes (both empty and non empty) | |
* | |
*/ | |
public interface CommonList extends FeatureStructure { | |
public final static String _FeatName_head = "head"; | |
public final static String _FeatName_tail = "tail"; | |
static final List<String> EMPTY_LIST_STRING = Collections.emptyList(); | |
/** | |
* Get the nth node. | |
* @param i - | |
* @return the nth node, which may be an "empty" node | |
*/ | |
default CommonList getNthNode(int i) { | |
if (this instanceof EmptyList) { | |
throw new CASRuntimeException(CASRuntimeException.JCAS_GET_NTH_ON_EMPTY_LIST, "EmptyList"); | |
} | |
if (i < 0) { | |
throw new CASRuntimeException(CASRuntimeException.JCAS_GET_NTH_NEGATIVE_INDEX, i); | |
} | |
int originali = i; | |
CommonList node = this; | |
for (;; i--) { | |
if (i == 0) { | |
return node; | |
} | |
node = node.getCommonTail(); | |
if (node instanceof EmptyList) { | |
throw new CASRuntimeException(CASRuntimeException.JCAS_GET_NTH_PAST_END, originali); | |
} | |
} | |
} | |
default CommonList getNonEmptyNthNode(int i) { | |
CommonList node = getNthNode(i); | |
if (node instanceof EmptyList) { | |
throw new CASRuntimeException(CASRuntimeException.JCAS_GET_NTH_PAST_END, i); | |
} | |
return node; | |
} | |
/** | |
* length of a list, handling list loops. | |
* returns the number of unique nodes in the list | |
* @return the number of items in the list | |
*/ | |
default int getLength() { | |
final int[] length = {0}; | |
try { | |
walkList( n -> length[0]++, () -> {}); | |
} catch (SAXException e) { | |
// never happen | |
} | |
return length[0]; | |
} | |
default void walkList(Consumer_withSaxException<NonEmptyList> consumer, Runnable foundLoop) throws SAXException { | |
final Set<CommonList> visited = Collections.newSetFromMap(new IdentityHashMap<>()); | |
CommonList node = this; | |
while (node instanceof NonEmptyList) { | |
consumer.accept((NonEmptyList) node); | |
node = node.getCommonTail(); | |
if (node == null) { | |
break; | |
} | |
if (visited.contains(node) && foundLoop != null) { | |
foundLoop.run(); | |
} | |
} | |
} | |
CommonList createNonEmptyNode(); | |
CommonList getEmptyList(); | |
/** | |
* overridden in nonempty nodes | |
* Return the head value of a list as a string suitable for serialization. | |
* | |
* For FeatureStructure values, return the _id. | |
* @return value suitable for serialization | |
*/ | |
default String get_headAsString() { | |
throw new UnsupportedOperationException(); | |
}; | |
/** | |
* overridden in nonempty nodes | |
* used when deserializing | |
* @param v value to set, as a string | |
*/ | |
default void set_headFromString(String v) { | |
throw new UnsupportedOperationException(); | |
} | |
/** | |
* insert a new nonempty node following this node | |
* @return the new node | |
*/ | |
default CommonList insertNode() { | |
assert(this instanceof NonEmptyList); | |
CommonList newNode = createNonEmptyNode(); | |
CommonList tail = getCommonTail(); | |
setTail(newNode); | |
newNode.setTail(tail); | |
return newNode; | |
} | |
/** | |
* Creates a new node and pushes it onto the front of the existing node | |
* @return the new node | |
*/ | |
default CommonList pushNode() { | |
CommonList newNode = createNonEmptyNode(); | |
newNode.setTail(this); | |
return newNode; | |
} | |
/** | |
* default impl for empty and nonempty lists | |
* @return - instance of CommonList | |
* | |
* This has to be named differently from getTail, otherwise the | |
* "default" method in the interface appears as declared method in reflection named getTail which conflicts | |
* with the one returning a specific typed value | |
*/ | |
default CommonList getCommonTail() { | |
if (this instanceof NonEmptyFloatList) { | |
return ((NonEmptyFloatList)this).getTail(); | |
} | |
if (this instanceof NonEmptyIntegerList) { | |
return ((NonEmptyIntegerList)this).getTail(); | |
} | |
if (this instanceof NonEmptyStringList) { | |
return ((NonEmptyStringList)this).getTail(); | |
} | |
if (this instanceof NonEmptyFSList) { | |
return ((NonEmptyFSList)this).getTail(); | |
} | |
throw new UnsupportedOperationException(); | |
} | |
// there is no common getTail, because each one has a different return type | |
// the impl of setTail(CommonList v) is split: | |
// the default impl throws UnsupportedOperationException; | |
// each kind of non-empty list class has its own impl | |
/** | |
* default | |
* @param v - | |
*/ | |
default void setTail(CommonList v) { | |
throw new UnsupportedOperationException(); | |
} | |
/** | |
* List to String for XMI and JSON serialization, for the special format where | |
* all the list elements are in one serialized item | |
* | |
* Go thru a list, calling the ListOutput append method to append strings (to arrays, or string buffers) | |
* Stop at the end node, or a null, or a loop (no error reported here) | |
* @param sharedData - | |
* @param cds - | |
* @param out - a Consumer of strings | |
*/ | |
default void anyListToOutput(XmiSerializationSharedData sharedData, CasSerializerSupport.CasDocSerializer cds, Consumer<String> out) { | |
// final int type = cas.getHeapValue(curNode); | |
// final int headFeat = getHeadFeatCode(type); | |
// final int tailFeat = getTailFeatCode(type); | |
// final int neListType = getNeListType(type); | |
final Set<CommonList> visited = Collections.newSetFromMap(new IdentityHashMap<>()); | |
CommonList curNode = this; | |
while (curNode != null) { | |
if (curNode instanceof EmptyList) { | |
break; // would be the end element. | |
} | |
if (!visited.add(curNode)) { | |
break; // hit loop | |
} | |
// out.accept(curNode.get_headAsString()); | |
if (curNode instanceof NonEmptyFSList) { | |
TOP val = ((NonEmptyFSList)curNode).getHead(); | |
if (val == null) { | |
if (sharedData != null) { | |
OotsElementData oed = sharedData.getOutOfTypeSystemFeatures((TOP)curNode); | |
if (oed != null) { | |
assert oed.attributes.size() == 1; //only the head feature can possibly be here | |
XmlAttribute attr = oed.attributes.get(0); | |
assert CAS.FEATURE_BASE_NAME_HEAD.equals(attr.name); | |
out.accept(attr.value); | |
} else { | |
out.accept("0"); | |
} | |
} else { | |
out.accept("0"); | |
} | |
} else { | |
out.accept(cds.getXmiId(val)); | |
} | |
} else { | |
// is instance of float, integer, or string list | |
out.accept(curNode.get_headAsString()); | |
} | |
curNode = curNode.getCommonTail(); | |
} // end of while loop | |
} | |
default List<String> anyListToStringList(XmiSerializationSharedData sharedData, CasSerializerSupport.CasDocSerializer cds) { | |
final List<String> list = new ArrayList<String>(); | |
anyListToOutput(sharedData, cds, s -> list.add(s)); | |
return list; | |
} | |
/* (non-Javadoc) | |
* @see org.apache.uima.cas.FeatureStructure#id() | |
*/ | |
@Override | |
default int _id() { | |
return ((FeatureStructureImplC)this)._id(); | |
} | |
} |