com.day.durbo.DurboInput Maven / Gradle / Ivy
/*
* Copyright 1997-2008 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.durbo;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.jcr.NamespaceException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import com.day.durbo.impl.DurboInputStream;
import com.day.durbo.io.RegionFileInputStream;
/**
* The DurboInput
class implements a reader on a inputstream that
* contains durbo-serialized data. {@see DurboOutput} for a more detailed
* description of this protocol.
*/
public class DurboInput implements DurboConstants, DurboNamespaceResolver {
/**
* the 'undefined' property type.
*
* @deprecated Use {@link PropertyType#UNDEFINED} instead
*/
public static final int PROPERTY_TYPE_UNDEFINED = PropertyType.UNDEFINED;
/**
* the 'string' property type.
*
* @deprecated Use {@link PropertyType#STRING} instead
*/
public static final int PROPERTY_TYPE_STRING = PropertyType.STRING;
/**
* the 'binary' property type.
*
* @deprecated Use {@link PropertyType#BINARY} instead
*/
public static final int PROPERTY_TYPE_BINARY = PropertyType.BINARY;
/**
* the 'long' property type.
*
* @deprecated Use {@link PropertyType#LONG} instead
*/
public static final int PROPERTY_TYPE_LONG = PropertyType.LONG;
/**
* the 'double' property type.
*
* @deprecated Use {@link PropertyType#DOUBLE} instead
*/
public static final int PROPERTY_TYPE_DOUBLE = PropertyType.DOUBLE;
/**
* the 'date' property type.
*
* @deprecated Use {@link PropertyType#DATE} instead
*/
public static final int PROPERTY_TYPE_DATE = PropertyType.DATE;
/**
* the 'boolean' property type.
*
* @deprecated Use {@link PropertyType#BOOLEAN} instead
*/
public static final int PROPERTY_TYPE_BOOLEAN = PropertyType.BOOLEAN;
/**
* the 'name' property type.
*
* @deprecated Use {@link PropertyType#NAME} instead
*/
public static final int PROPERTY_TYPE_NAME = PropertyType.NAME;
/**
* the 'path' property type.
*
* @deprecated Use {@link PropertyType#PATH} instead
*/
public static final int PROPERTY_TYPE_PATH = PropertyType.PATH;
/**
* the 'reference' property type.
*
* @deprecated Use {@link PropertyType#REFERENCE} instead
*/
public static final int PROPERTY_TYPE_REFERENCE = PropertyType.REFERENCE;
/**
* the input stream to read from
*/
private DurboInputStream in;
/**
* the current version
*/
private double version = PROTOCOL_VERSION_1;
/**
* the encoding
*/
private String encoding = "";
/**
* the content type
*/
private String contentType = "";
/**
* map of namespaces by prefix
*/
private Map namespaceByPrefix = new HashMap();
/**
* map of namespaces by prefix
*/
private Map namespaceByURI = new HashMap();
/**
* Creates a new DurboInput
on the specified file. This
* also reads the header to check if the underlying stream is of the correct
* format. In difference to the more generic {@link #DurboInput(InputStream)}
* constructor this operates on a file an can therefor be optimized.
*
* @param file underlying file
* @throws IOException if an I/O error occurs
*/
public DurboInput(File file) throws IOException {
this(new RegionFileInputStream(file));
}
/**
* Creates a new DurboInput
on the specified input stream. This
* also reads the header to check if the underlying stream is of the correct
* format.
*
* @param inputStream the input stream
* @throws IOException if an I/O error occurrs
*/
public DurboInput(InputStream inputStream) throws IOException {
in = new DurboInputStream(inputStream);
namespaceByPrefix.put("", "");
namespaceByURI.put("", "");
Element elem = read();
if (elem == null) {
throw new IOException("Protocol Header expected.");
}
if (!PROTOCOL_HEADER.equals(elem.name())) {
throw new IOException("Wrong Protocol Header Property: " + elem.name());
}
version = ((Property) elem).getValues()[0].getDouble();
if (version > PROTOCOL_VERSION_2_1) {
throw new IOException("Wrong Protocol Version: " + elem.getString());
}
if (version >= PROTOCOL_VERSION_2) {
elem = read();
if (!PROTOCOL_CONTENT_TYPE.equals(elem.name())) {
throw new IOException("Wrong protocol. " + PROTOCOL_CONTENT_TYPE + " expected.");
}
contentType = ((Property) elem).getValues()[0].getString();
elem = read();
if (!PROTOCOL_ENCODING.equals(elem.name())) {
throw new IOException("Wrong protocol. " + PROTOCOL_ENCODING + " expected.");
}
encoding = ((Property) elem).getValues()[0].getString();
if (encoding.equals("zip")) {
in.enableDecompression();
}
}
}
public String getEncoding() {
return encoding;
}
public String getContentType() {
return contentType;
}
/**
* Returns the version
*
* @return the version
*/
public double getVersion() {
return version;
}
/**
* Reads an element from this input
*
* @return an durbo element or null
if a end of the input
* @throws IOException if an I/O error occurs or if the data do not
* match the protocol specification
*/
public Element read() throws IOException {
int type = in.readType();
if (type == -1) {
return null;
}
if ((type & PROPERTY) > 0) {
String name = in.readString();
boolean isMulti = (type & MULTIPLE) > 0;
DurboValue[] values = isMulti ? new DurboValue[in.readInt()] : new DurboValue[1];
type &= PROPERTY_TYPE_MASK;
for (int i = 0; i < values.length; i++) {
switch (type) {
case PropertyType.STRING:
case PropertyType.LONG:
case PropertyType.DOUBLE:
case PropertyType.DATE:
case PropertyType.BOOLEAN:
case PropertyType.NAME:
case PropertyType.PATH:
case PropertyType.REFERENCE:
case PropertyType.WEAKREFERENCE:
case PropertyType.DECIMAL:
case PropertyType.URI:
values[i] = new DurboValue(type, in.readBinary());
break;
default:
if (version >= PROTOCOL_VERSION_2) {
throw new IOException("unsupported type: " + type);
}
type = PropertyType.BINARY;
// no break;
case PropertyType.BINARY:
values[i] = in.readBinaryValue();
break;
}
}
return new Property(name, type, isMulti, values);
} else if (type == NODE_START) {
return new Node(in.readString());
} else if (type == NODE_END) {
return new Node();
} else if (type == NAMESPACE) {
String prefix = in.readString();
String uri = in.readString();
namespaceByPrefix.put(prefix, uri);
namespaceByURI.put(uri, prefix);
return read();
} else {
throw new IOException("Unknown element type: " + type);
}
}
/**
* skips a node
*
* @throws IOException if an I/O error occurs
*/
public void skipNode() throws IOException {
DurboInput.Element elem;
while ((elem = read()) != null) {
if (elem.isNodeStart()) {
skipNode();
} else if (elem.isNodeEnd()) {
return;
}
}
throw new EOFException("Unexpected end of input");
}
/**
* Returns the namespace uri for the given namespace
*
* @param prefix the namespace prefix
* @return the namespace uri
*/
public String getURI(String prefix) {
return namespaceByPrefix.get(prefix);
}
/**
* returns the namespace prefix for the given uri
*
* @param uri the namespace uri
* @return the namespace prefix
* @throws NamespaceException
*/
public String getPrefix(String uri) throws NamespaceException {
return namespaceByURI.get(uri);
}
public String[] getPrefixes() {
return namespaceByPrefix.keySet().toArray(new String[namespaceByPrefix.size()]);
}
public String[] getURIs() {
return namespaceByURI.keySet().toArray(new String[namespaceByURI.size()]);
}
public class Property extends Element {
/**
* the property type
*/
private final int type;
/**
* flag, indicating if the property was multiple
*/
private final boolean isMultiple;
/**
* values
*/
private final DurboValue[] values;
public Property(String name, int type, boolean multiple, DurboValue[] values) {
super(name);
this.type = type;
isMultiple = multiple;
this.values = values;
}
public int getType() {
return type;
}
public boolean isMultiple() {
return isMultiple;
}
public DurboValue[] getValues() {
return values;
}
public Value[] getJcrValues(ValueFactory factory)
throws IOException, RepositoryException {
Value[] ret = new Value[values.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = values[i].toJcrValue(factory);
}
return ret;
}
public boolean isProperty() {
return true;
}
public boolean isNodeStart() {
return false;
}
public boolean isNodeEnd() {
return false;
}
public String getString() {
try {
return (values.length > 0) ? values[0].getString() : "";
} catch (IOException e) {
return null;
}
}
public boolean wasBinary() {
return type == PropertyType.BINARY;
}
public boolean wasString() {
return type != PropertyType.BINARY;
}
public long size() {
return 0;
}
}
public class Node extends Element {
private final boolean isStart;
public Node(String name) {
this(name, true);
}
public Node() {
this(null, false);
}
public Node(String name, boolean isNodeStart) {
super(name);
isStart = isNodeStart;
}
public boolean isProperty() {
return false;
}
public boolean isNodeStart() {
return isStart;
}
public boolean isNodeEnd() {
return !isStart;
}
public String getString() {
return null;
}
public boolean wasBinary() {
return false;
}
public boolean wasString() {
return false;
}
public long size() {
return 0;
}
}
/**
* Inner class that represents one element of the Durbo protocol
*/
abstract public class Element {
/**
* the name of this element
*/
private final String localName;
/**
* the namespace prefix
*/
private final String prefix;
/**
* Creates a new element
* @param name element name
*/
private Element(String name) {
if (name == null) {
this.localName = null;
this.prefix = null;
} else {
int pos = name.indexOf(':');
if (pos > 0) {
this.prefix = name.substring(0, pos);
this.localName = name.substring(pos + 1);
} else {
this.prefix = null;
this.localName = name;
}
}
}
/**
* Returns the name of this element
*
* @return the name of this element
*/
public String name() {
return prefix == null ? localName : prefix + ":" + localName;
}
/**
* returns the prefix of this element
*
* @return the prefix.
*/
public String prefix() {
return prefix;
}
/**
* returns the namespace uri for this element
*
* @return the namespace uri.
*/
public String uri() {
return prefix == null ? null : getURI(prefix);
}
/**
* returns the local name of this element
*
* @return the local name.
*/
public String localName() {
return localName;
}
/**
* Checks if this element is a property
*
* @return true
if this element is a property;
* false
otherwise
*/
abstract public boolean isProperty();
/**
* Checks if this element is a node start
*
* @return true
if this element is a node start;
* false
otherwise
*/
abstract public boolean isNodeStart();
/**
* Checks if this element is a node end
*
* @return true
if this element is a node end; false
* otherwise
*/
abstract public boolean isNodeEnd();
/**
* Convenience method
*
* @return the string.
*/
abstract public String getString();
/**
* @return the debug representation if this element
* @deprecated use {@link #getString()} instead.
*/
public String toString() {
if (isProperty()) {
return "Property(" + name() + ")";
} else if (isNodeStart()) {
return "NodeStart(" + name() + ")";
} else {
return "NodeEnd()";
}
}
/**
* Checks if this elem is a property and has binary content.
*
* @return true
if this element has binary content.
* @deprecated use {@link Property#getType()} instead.
*/
abstract public boolean wasBinary();
/**
* Checks if this elem is a property and has string content.
*
* @return true
if this element has string content.
* @deprecated use {@link Property#getType()} instead.
*/
abstract public boolean wasString();
/**
* Returns the size of the this element if it's a property.
*
* @return the size of the this element if it's a property.
* @deprecated use {@link Property#getValues().length()} instead.
*/
abstract public long size();
}
}