org.apache.vinci.transport.Frame Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jVinci Show documentation
Show all versions of jVinci Show documentation
This is a non-standard protocol for higher efficiency than
SOAP, used by the base UIMA Collection processing manager for supporting
networked deployment. See UIMA-AS as a more modern alternative supporting
more standard protocols.
/*
* 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.vinci.transport;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.vinci.debug.Debug;
import org.apache.vinci.transport.util.XMLConverter;
/**
* Frame is an abstract class that is intended to be extended to implement a simple & lean
* (restricted) XML document model. A Frame is only capable of representing XML documents with no
* attributes or processing instructions. Applications which require attributes should use the
* org.apache.vinci.transport.document.AFrame document model instead.
*
* Frame "decorates" its descendents with several type-safe adder methods for building the XML
* document. It requires its descendents implement only a single generic adder method [add(String,
* FrameComponent)] and a getter for retreiving fields of the document by their position
* [getKeyValuePair(int)]. The Frame descendent QueryableFrame provides additional getter methods.
* QueryableFrame should be extended instead of Frame if this more powerful query support is
* necessary.
*
* Frame also implements the Transportable interface and provides a default marshaller for
* converting to and from XTalk wire format. The marshaller is pluggable to support marshalling to
* and from other formats if so desired, or to support optimized (e.g. native) implementation.
*
* Typically you will use VinciFrame, a concrete descendent of the (Queryable)Frame class, for most
* of your code. You may however also wish to implement specialized Frame descendents for optimized
* and/or type-safe handling of particular queries. For example, see ResolveResult and ServeonResult
* (in package org.apache.vinci.transport.vns.client), which do this for the two most common VNS
* queries. Automated stub generators may also wish to extend Frame to generate Java object to Vinci
* XML document adapters.
*
*/
public abstract class Frame extends FrameComponent implements Transportable {
static private final String XML_INDENT = " ";
static private FrameTransporter parser = new XTalkTransporter();
protected Frame() {
}
/**
* This method lets you replace the default XTalk marshaller with another one, for example if you
* want to use a different wire format (such as XML/SOAP), or if you want to easily exploit an
* optimized/native implementation.
*
* @param transporter
* The new marshaller to plug in.
*/
static public void setFrameTransporter(FrameTransporter transporter) {
parser = transporter;
}
/**
* Get the currently installed document marshaller.
*
* @return The currently installed document marshaller.
*/
static public FrameTransporter getFrameTransporter() {
return parser;
}
/**
* Add a tagged value to this frame (value is either a Frame or FrameLeaf). Frames that support
* marshalling from a stream must implement this method.
*
* While the method is conceptually abstract, I provide a default error-only implementation in
* cases where marshalling the document to a stream is not necessary (e.g. the document is used as
* an input source only).
*
* @param tag
* The tag name with which to associate the value.
* @param val
* The (Frame | FrameLeaf) value to associate with the tag.
* @throws UnsupportedOperationException not implemented
*/
/* abstract */public void add(String tag, FrameComponent val) {
throw new UnsupportedOperationException("not implemented");
}
/**
* Return the specified KeyValue pair. Frames that support marshalling to (but not from) a stream
* via the Transportable interface must implement this method. We chose this simple iteration
* method over providing an interator object since it doesn't require allocating any new objects.
*
* While conceptually abstract, I provide a default error-only implementation to avoid the need to
* define it in cases where alternative getters are provided for querying the document, and the
* object is never marshalled to a stream.
*
* @param which
* The index of the KeyValuePair to retrieve.
* @throws UnsupportedOperationException not implemented
* @return The requested KeyValuePair.
*/
/* abstract */public KeyValuePair getKeyValuePair(int which) {
throw new UnsupportedOperationException("not implemented");
}
/**
* Return the number of key/value pairs within this frame. Frames that support marshalling to (but
* not from) a stream via the Transportable interface must implement this method.
*
* While conceptually abstract, I provide a default error-only implementation to avoid the need to
* define it in cases where alternative getters are provided for querying the document, and the
* object is never marshalled to a stream.
*
* @throws UnsupportedOperationException not implemented
* @return The total number of key/value pairs in this frame.
*/
/* abstract */public int getKeyValuePairCount() {
throw new UnsupportedOperationException("not implemented");
}
/**
* Populate this document using the given InputStream and the installed marshaller.
*
* @param is
* The input stream to read from.
* @throws IOException
* Can come from the underlying input stream.
* @throws UnsupportedOperationException
* if this document model does not support key addition.
* @return The first key/value pair encountered, if it comes from the Vinci namespace.
*
* @pre is != null
*/
public KeyValuePair fromStream(InputStream is) throws IOException, EOFException {
return parser.fromStream(is, this);
}
/**
* Write this document to the given output stream using the installed marshaller.
*
* @param os
* The stream to where the document is written.
* @throws IOException
* Can come from the underlying output stream.
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
*
* @pre os != null
*/
public void toStream(OutputStream os) throws IOException {
parser.toStream(os, this);
}
/**
* Represent the document as a string (equivlent to toXML()).
*
* @return The document in String format.
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
*/
public String toString() {
return toXML();
}
/**
* Convert the document to XML. Performs limited pretty printing -- if you don't want any pretty
* printing, use toRawXML().
*
* @return The document in XML format as a string.
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
*/
public String toXML() {
return toXML(new StringBuffer()).toString();
}
/**
* Convert the document to XML without any pretting printing.
*
* @return The document in XML format as a string.
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
* @since 2.1.2
*/
public String toRawXML() {
return toRawXML(new StringBuffer()).toString();
}
/**
* Convert the document to XML without any pretting printing.
*
* @param buf
* The StringBuffer to append the document text to.
* @return The StringBuffer that was provided as input
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
* @since 2.1.2
*/
public StringBuffer toRawXML(StringBuffer buf) {
buf.append("");
toRawXMLWork(buf);
buf.append(" ");
return buf;
}
public void toRawXMLWork(StringBuffer rval) {
KeyValuePair keyVal = null;
int total = getKeyValuePairCount();
for (int i = 0; i < total; i++) {
keyVal = getKeyValuePair(i);
boolean pcdata = TransportConstants.PCDATA_KEY.equals(keyVal.key);
if (!pcdata) {
rval.append('<').append(keyVal.key).append(attributeString(keyVal.value));
if (keyVal.value instanceof Frame && ((Frame) keyVal.value).getKeyValuePairCount() == 0) {
rval.append("/>");
continue;
} else {
rval.append('>');
}
}
if (keyVal.value instanceof FrameLeaf) {
XMLConverter.convertStringToXMLString(((FrameLeaf) keyVal.value).toString(), rval);
} else {
((Frame) keyVal.value).toRawXMLWork(rval);
}
if (!pcdata) {
rval.append("").append(keyVal.key).append('>');
}
}
}
/**
* Append the document, in XML format, to the provided StringBuffer.
*
* @param buf
* The StringBuffer where the document is appended.
* @return The same StringBuffer provided to this method using the buf argument.
* @throws UnsupportedOperationException
* if this document model does not support key iteration.
*
* @pre buf != null
*/
public StringBuffer toXML(StringBuffer buf) {
buf.append("\n");
// ^^ Note that Frames do not support XML namespaces, however the above xml
// namespace is in some sense "implicit" for every Vinci document.
toXML(buf, 0);
buf.append(" \n");
return buf;
}
/**
* Factory method used by fromStream when it needs to create a frame leaf. Default implementation
* creates a regular FrameLeaf.
*
* @param array -
* @return the created FrameLeaf.
*
* @pre array != null
*/
public FrameLeaf createFrameLeaf(byte[] array) {
return new FrameLeaf(array, false);
}
/**
* Factory method used by fromStream when it needs to create a sub-frame. Default implementation
* creates a subframe of the same type as the current frame.
* @param tag_name -
* @param initialCapacity -
* @return the created sub-frame.
* @throws UnsupportedOperationException
* if the getClass().newInstance() call on this object results in an exception.
*
* @pre tag_name != null
* @pre initialCapacity ≥ 0
*/
public Frame createSubFrame(String tag_name, int initialCapacity) {
try {
return (Frame) this.getClass().newInstance();
}
// Neither error case should take place, so we handle them by converting them to
// unchecked exceptions.
catch (Exception e) {
Debug.reportException(e);
throw new UnsupportedOperationException("createSubFrame() failed: " + e);
}
}
/**
* Helper method used by toXML()
*
* @pre c != null
*/
private String attributeString(FrameComponent c) {
Attributes attributes = c.getAttributes();
if (attributes == null) {
return "";
}
StringBuffer buf = new StringBuffer(80);
for (int i = 0; i < attributes.getKeyValuePairCount(); i++) {
KeyValuePair k = attributes.getKeyValuePair(i);
buf.append(" ");
buf.append(k.key);
buf.append("=\"");
XMLConverter.simpleConvertStringToXMLString(k.value.toString(), buf);
buf.append('\"');
}
return buf.toString();
}
/**
* Helper method for toXML(StringBuffer).
*
* @pre rval != null
* @pre offset ≥ 0
* @param rval -
* @param offset -
*/
protected void toXML(StringBuffer rval, int offset) {
KeyValuePair keyVal = null;
int total = getKeyValuePairCount();
for (int i = 0; i < total; i++) {
keyVal = getKeyValuePair(i);
for (int j = 0; j <= offset; j++) {
rval.append(XML_INDENT);
}
boolean pcdata = TransportConstants.PCDATA_KEY.equals(keyVal.key);
if (!pcdata) {
rval.append('<').append(keyVal.key).append(attributeString(keyVal.value));
if (keyVal.value instanceof Frame && ((Frame) keyVal.value).getKeyValuePairCount() == 0) {
rval.append("/>\n");
continue;
} else {
rval.append('>');
}
}
if (keyVal.value instanceof FrameLeaf) {
XMLConverter.convertStringToXMLString(((FrameLeaf) keyVal.value).toString(), rval);
} else {
rval.append('\n');
((Frame) keyVal.value).toXML(rval, offset + 1);
for (int j = 0; j <= offset; j++) {
rval.append(XML_INDENT);
}
}
if (!pcdata) {
rval.append("").append(keyVal.key).append('>');
}
rval.append('\n');
}
}
/**
* Decorator method for adding float-valued tags.
*
* @param key
* The key to be associated with the value.
* @param val -
* @return This frame
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, float val) {
add(key, new FrameLeaf(val));
return this;
}
/**
* Decorator method for adding float-array valued tags.
*
* @param key
* The key to be associated with the value.
* @param val -
* @return This frame
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, float[] val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding double valued tags.
*
* @param key
* The key to be associated with the value.
* @param val -
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
* @return This frame
*
* @pre key != null
*/
public Frame fadd(String key, double val) {
add(key, new FrameLeaf(val));
return this;
}
/**
* Decorator method for adding double-array valued tags. Adding a null array results in a no-op.
*
* @param key
* The key to be associated with the value.
* @param val
* The array to add. The array is immediately converted to string representation.
* @return This frame
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, double[] val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding int valued tags.
*
* @param key
* The key to be associated with the value.
* @param val
* The int to add.
* @return This frame
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, int val) {
add(key, new FrameLeaf(val));
return this;
}
/**
* Decorator method for adding int-array valued tags. Adding a null value results in a no-op.
*
* @param key
* The key to be associated with the value.
* @param val
* The array to add. The array is immediately converted to string representation.
* @return This frame
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, int[] val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding long valued tags.
*
* @param key
* The key to be associated with the value.
* @param val
* The long value to add.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, long val) {
add(key, new FrameLeaf(val));
return this;
}
/**
* Decorator method for adding long-array valued tags. Adding a null value results in a no-op.
*
* @param key
* The key to be associated with the value.
* @param val
* The array to add. The array is immediately converted to string representation.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, long[] val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding String valued tags.
*
* @param key
* The key to be associated with the value.
* @param val
* The string to add.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, String val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding String-array valued tags. Adding a null value results in a no-op.
*
* This implementation will use the '#' char as the string separator. If the '#' char is used in
* some string, then the implementation generates a separator that is not contained by any string
* and prepends the separator to the string for extraction. The generated separator always will
* begin and end with the character '#'. This allows arbitrary string arrays to be sent as a
* single string without separator conflicts.
*
* @param key
* The key to be associated with the value.
* @param val
* The string to add.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
* @return This frame.
*
* @pre key != null
* @pre { for (int i = 0; i < val.length; i++) $assert(val[i] != null, "array elements are
* non-null"); }
*/
public Frame fadd(String key, String[] val) {
if (val != null) {
add(key, new FrameLeaf(val));
}
return this;
}
/**
* Decorator method for adding binary valued tags. Encodes the data in Base64. Adding a null value
* results in a no-op.
*
* @param key
* The key to be associated with the value.
* @param val
* The binary data to add (will be Base64 encoded).
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, byte[] val) {
if (val != null) {
add(key, new FrameLeaf(val, true));
}
return this;
}
/**
* Decorator method for adding boolean valued tags.
*
* @param key
* The key to be associated with the value.
* @param val
* The boolean value to add.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, boolean val) {
add(key, new FrameLeaf(val));
return this;
}
/**
* Decorator method for adding Frame-valued tags. Adding a null value results in a no-op.
*
* @param key
* The key to be associated with the value.
* @param val
* The sub-frame to add. Note this frame is not copied.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame fadd(String key, Frame val) {
if (val != null) {
add(key, val);
}
return this;
}
/**
* Decorator method for adding a valueless tag.
*
* @param key
* The key name.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition, or creation of empty sub-frames.
*
* @pre key != null
*/
public Frame fadd(String key) {
add(key, createSubFrame(key, 0));
return this;
}
/**
* This is a hack method which allows you to add binary-valued tags to Frames in a manner such
* that there is no textual encoding overhead of that binary data. This is NOT necessarily
* XTalk-1.0 compatible which formally requires only UTF-8, but it still works. Binary data added
* using this method can be retrieved using QueryableFrame/VinciFrame getter method
* fgetTrueBinary(String). Adding a null value results in a no-op.
*
* WARNING: if the default XTalk parser is replaced with another one, applications that depend on
* this method may break!
*
* WARNING #2: This method should only be used when performance hit of Base64 encoding binary data
* [as performed by fadd(String, byte[])] is unacceptable.
*
* WARNING #3: The byte array is NOT copied, thus it is up to the caller to ensure that the byte
* array cannot be modified by external code after passed in to this object.
*
* @param key
* The key to be associated with the value.
* @param val
* The byte array to be added to the frame. Note the array is NOT copied or converted in
* any way.
* @return This frame.
* @throws UnsupportedOperationException
* if this document model doesn't support key addition.
*
* @pre key != null
*/
public Frame faddTrueBinary(String key, byte[] val) {
if (val != null) {
add(key, new FrameLeaf(val, false));
}
return this;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy