/*
* Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved.
*
* The Apache Software License, Version 1.1
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Caucho Technology (http://www.caucho.com/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Hessian", "Resin", and "Caucho" must not be used to
* endorse or promote products derived from this software without prior
* written permission. For written permission, please contact
* info@caucho.com.
*
* 5. Products derived from this software may not be called "Resin"
* nor may "Resin" appear in their names without prior written
* permission of Caucho Technology.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Scott Ferguson
*/
package com.caucho.burlap.io;
import com.caucho.hessian.io.Deserializer;
import com.caucho.hessian.io.HessianRemoteResolver;
import com.caucho.hessian.io.SerializerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
/**
* Input stream for Burlap requests.
*
* <p>BurlapInput is unbuffered, so any client needs to provide
* its own buffering.
*
* <pre>
* InputStream is = ...; // from http connection
* BurlapInput in = new BurlapInput(is);
* String value;
*
* in.startReply(); // read reply header
* value = in.readString(); // read string value
* in.completeReply(); // read reply footer
* </pre>
*/
public class BurlapInput extends AbstractBurlapInput {
private static int []base64Decode;
public final static int TAG_EOF = -1;
public final static int TAG_NULL = 0;
public final static int TAG_BOOLEAN = 1;
public final static int TAG_INT = 2;
public final static int TAG_LONG = 3;
public final static int TAG_DOUBLE = 4;
public final static int TAG_DATE = 5;
public final static int TAG_STRING = 6;
public final static int TAG_XML = 7;
public final static int TAG_BASE64 = 8;
public final static int TAG_MAP = 9;
public final static int TAG_LIST = 10;
public final static int TAG_TYPE = 11;
public final static int TAG_LENGTH = 12;
public final static int TAG_REF = 13;
public final static int TAG_REMOTE = 14;
public final static int TAG_CALL = 15;
public final static int TAG_REPLY = 16;
public final static int TAG_FAULT = 17;
public final static int TAG_METHOD = 18;
public final static int TAG_HEADER = 19;
public final static int TAG_NULL_END = TAG_NULL + 100;
public final static int TAG_BOOLEAN_END = TAG_BOOLEAN + 100;
public final static int TAG_INT_END = TAG_INT + 100;
public final static int TAG_LONG_END = TAG_LONG + 100;
public final static int TAG_DOUBLE_END = TAG_DOUBLE + 100;
public final static int TAG_DATE_END = TAG_DATE + 100;
public final static int TAG_STRING_END = TAG_STRING + 100;
public final static int TAG_XML_END = TAG_XML + 100;
public final static int TAG_BASE64_END = TAG_BASE64 + 100;
public final static int TAG_MAP_END = TAG_MAP + 100;
public final static int TAG_LIST_END = TAG_LIST + 100;
public final static int TAG_TYPE_END = TAG_TYPE + 100;
public final static int TAG_LENGTH_END = TAG_LENGTH + 100;
public final static int TAG_REF_END = TAG_REF + 100;
public final static int TAG_REMOTE_END = TAG_REMOTE + 100;
public final static int TAG_CALL_END = TAG_CALL + 100;
public final static int TAG_REPLY_END = TAG_REPLY + 100;
public final static int TAG_FAULT_END = TAG_FAULT + 100;
public final static int TAG_METHOD_END = TAG_METHOD + 100;
public final static int TAG_HEADER_END = TAG_HEADER + 100;
private static HashMap _tagMap;
private static Field _detailMessageField;
protected SerializerFactory _serializerFactory;
protected ArrayList _refs;
// the underlying input stream
private InputStream _is;
// a peek character
protected int _peek = -1;
// the method for a call
private String _method;
private int _peekTag;
private Throwable _replyFault;
protected StringBuffer _sbuf = new StringBuffer();
protected StringBuffer _entityBuffer = new StringBuffer();
protected Calendar _utcCalendar;
protected Calendar _localCalendar;
/**
* Creates an uninitialized Burlap input stream.
*/
public BurlapInput()
{
}
/**
* Creates a new Burlap input stream, initialized with an
* underlying input stream.
*
* @param is the underlying input stream.
*/
public BurlapInput(InputStream is)
{
init(is);
}
/**
* Sets the serializer factory.
*/
public void setSerializerFactory(SerializerFactory factory)
{
_serializerFactory = factory;
}
/**
* Gets the serializer factory.
*/
public SerializerFactory getSerializerFactory()
{
return _serializerFactory;
}
/**
* Initialize the burlap stream with the underlying input stream.
*/
public void init(InputStream is)
{
_is = is;
_method = null;
_peek = -1;
_peekTag = -1;
_refs = null;
_replyFault = null;
if (_serializerFactory == null)
_serializerFactory = new SerializerFactory();
}
/**
* Returns the calls method
*/
public String getMethod()
{
return _method;
}
/**
* Returns any reply fault.
*/
public Throwable getReplyFault()
{
return _replyFault;
}
/**
* Starts reading the call
*
* <pre>
* <burlap:call>
* <method>method</method>
* </pre>
*/
public void startCall()
throws IOException
{
readCall();
while ((readHeader() != null))
readObject();
readMethod();
}
/**
* Starts reading the call
*
* <p>A successful completion will have a single value:
*
* <pre>
* <burlap:call>
* </pre>
*/
public int readCall()
throws IOException
{
expectTag(TAG_CALL);
int major = 1;
int minor = 0;
return (major << 16) + minor;
}
/**
* Reads the method
*
* <pre>
* <method>method</method>
* </pre>
*/
public String readMethod()
throws IOException
{
expectTag(TAG_METHOD);
_method = parseString();
expectTag(TAG_METHOD_END);
return _method;
}
/**
* Completes reading the call
*
* <p>A successful completion will have a single value:
*
* <pre>
* </burlap:call>
* </pre>
*/
public void completeCall()
throws IOException
{
expectTag(TAG_CALL_END);
}
/**
* Reads a reply as an object.
* If the reply has a fault, throws the exception.
*/
public Object readReply(Class expectedClass)
throws Throwable
{
expectTag(TAG_REPLY);
int tag = parseTag();
if (tag == TAG_FAULT)
throw prepareFault();
else {
_peekTag = tag;
Object value = readObject(expectedClass);
expectTag(TAG_REPLY_END);
return value;
}
}
/**
* Starts reading the reply
*
* <p>A successful completion will have a single value:
*
* <pre>
* <burlap:reply>
* <value>
* </pre>
*/
public void startReply()
throws Throwable
{
expectTag(TAG_REPLY);
int tag = parseTag();
if (tag == TAG_FAULT)
throw prepareFault();
else
_peekTag = tag;
}
/**
* Prepares the fault.
*/
private Throwable prepareFault()
throws IOException
{
HashMap fault = readFault();
Object detail = fault.get("detail");
String message = (String) fault.get("message");
if (detail instanceof Throwable) {
_replyFault = (Throwable) detail;
if (message != null && _detailMessageField != null) {
try {
_detailMessageField.set(_replyFault, message);
} catch (Throwable e) {
}
}
return _replyFault;
}
else {
String code = (String) fault.get("code");
_replyFault = new BurlapServiceException(message, code, detail);
return _replyFault;
}
}
/**
* Completes reading the call
*
* <p>A successful completion will have a single value:
*
* <pre>
* </burlap:reply>
* </pre>
*/
public void completeReply()
throws IOException
{
expectTag(TAG_REPLY_END);
}
/**
* Reads a header, returning null if there are no headers.
*
* <pre>
* <header>value</header>
* </pre>
*/
public String readHeader()
throws IOException
{
int tag = parseTag();
if (tag == TAG_HEADER) {
_sbuf.setLength(0);
String value = parseString(_sbuf).toString();
expectTag(TAG_HEADER_END);
return value;
}
_peekTag = tag;
return null;
}
/**
* Reads a null
*
* <pre>
* <null></null>
* </pre>
*/
public void readNull()
throws IOException
{
int tag = parseTag();
switch (tag) {
case TAG_NULL:
expectTag(TAG_NULL_END);
return;
default:
throw expectedTag("null", tag);
}
}
/**
* Reads a boolean
*
* <pre>
* <boolean>0</boolean>
* <boolean>1</boolean>
* </pre>
*/
public boolean readBoolean()
throws IOException
{
int tag = parseTag();
boolean value;
switch (tag) {
case TAG_NULL:
value = false;
expectTag(TAG_NULL_END);
return value;
case TAG_BOOLEAN:
value = parseInt() != 0;
expectTag(TAG_BOOLEAN_END);
return value;
case TAG_INT:
value = parseInt() != 0;
expectTag(TAG_INT_END);
return value;
case TAG_LONG:
value = parseLong() != 0;
expectTag(TAG_LONG_END);
return value;
case TAG_DOUBLE:
value = parseDouble() != 0;
expectTag(TAG_DOUBLE_END);
return value;
default:
throw expectedTag("boolean", tag);
}
}
/**
* Reads a byte
*
* <pre>
* <int>value</int>
* </pre>
*/
public byte readByte()
throws IOException
{
return (byte) readInt();
}
/**
* Reads a short
*
* <pre>
* <int>value</int>
* </pre>
*/
public short readShort()
throws IOException
{
return (short) readInt();
}
/**
* Reads an integer
*
* <pre>
* <int>value</int>
* </pre>
*/
public int readInt()
throws IOException
{
int tag = parseTag();
int value;
switch (tag) {
case TAG_NULL:
value = 0;
expectTag(TAG_NULL_END);
return value;
case TAG_BOOLEAN:
value = parseInt();
expectTag(TAG_BOOLEAN_END);
return value;
case TAG_INT:
value = parseInt();
expectTag(TAG_INT_END);
return value;
case TAG_LONG:
value = (int) parseLong();
expectTag(TAG_LONG_END);
return value;
case TAG_DOUBLE:
value = (int) parseDouble();
expectTag(TAG_DOUBLE_END);
return value;
default:
throw expectedTag("int", tag);
}
}
/**
* Reads a long
*
* <pre>
* <long>value</long>
* </pre>
*/
public long readLong()
throws IOException
{
int tag = parseTag();
long value;
switch (tag) {
case TAG_NULL:
value = 0;
expectTag(TAG_NULL_END);
return value;
case TAG_BOOLEAN:
value = parseInt();
expectTag(TAG_BOOLEAN_END);
return value;
case TAG_INT:
value = parseInt();
expectTag(TAG_INT_END);
return value;
case TAG_LONG:
value = parseLong();
expectTag(TAG_LONG_END);
return value;
case TAG_DOUBLE:
value = (long) parseDouble();
expectTag(TAG_DOUBLE_END);
return value;
default:
throw expectedTag("long", tag);
}
}
/**
* Reads a float
*
* <pre>
* <double>value</double>
* </pre>
*/
public float readFloat()
throws IOException
{
return (float) readDouble();
}
/**
* Reads a double
*
* <pre>
* <double>value</double>
* </pre>
*/
public double readDouble()
throws IOException
{
int tag = parseTag();
double value;
switch (tag) {
case TAG_NULL:
value = 0;
expectTag(TAG_NULL_END);
return value;
case TAG_BOOLEAN:
value = parseInt();
expectTag(TAG_BOOLEAN_END);
return value;
case TAG_INT:
value = parseInt();
expectTag(TAG_INT_END);
return value;
case TAG_LONG:
value = parseLong();
expectTag(TAG_LONG_END);
return value;
case TAG_DOUBLE:
value = parseDouble();
expectTag(TAG_DOUBLE_END);
return value;
default:
throw expectedTag("double", tag);
}
}
/**
* Reads a date.
*
* <pre>
* <date>ISO-8609 date</date>
* </pre>
*/
public long readUTCDate()
throws IOException
{
int tag = parseTag();
if (tag != TAG_DATE)
throw error("expected date");
if (_utcCalendar == null)
_utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
long value = parseDate(_utcCalendar);
expectTag(TAG_DATE_END);
return value;
}
/**
* Reads a date.
*
* <pre>
* <date>ISO-8609 date</date>
* </pre>
*/
public long readLocalDate()
throws IOException
{
int tag = parseTag();
if (tag != TAG_DATE)
throw error("expected date");
if (_localCalendar == null)
_localCalendar = Calendar.getInstance();
long value = parseDate(_localCalendar);
expectTag(TAG_DATE_END);
return value;
}
/**
* Reads a string
*
* <pre>
* <string>value</string>
* </pre>
*/
public String readString()
throws IOException
{
int tag = parseTag();
String value;
switch (tag) {
case TAG_NULL:
expectTag(TAG_NULL_END);
return null;
case TAG_STRING:
_sbuf.setLength(0);
value = parseString(_sbuf).toString();
expectTag(TAG_STRING_END);
return value;
case TAG_XML:
_sbuf.setLength(0);
value = parseString(_sbuf).toString();
expectTag(TAG_XML_END);
return value;
default:
throw expectedTag("string", tag);
}
}
/**
* Reads an XML node.
*
* <pre>
* &xml;xml string</xml>
* </pre>
*/
public org.w3c.dom.Node readNode()
throws IOException
{
int tag = read();
switch (tag) {
case 'N':
return null;
case 'S':
case 's':
case 'X':
case 'x':
throw error("can't cope");
default:
throw expectedTag("string", tag);
}
}
/**
* Reads a byte array
*
* <pre>
* <base64>...</base64>
* </pre>
*/
public byte []readBytes()
throws IOException
{
int tag = parseTag();
switch (tag) {
case TAG_NULL:
expectTag(TAG_NULL_END);
return null;
case TAG_BASE64:
byte []data = parseBytes();
expectTag(TAG_BASE64_END);
return data;
default:
throw expectedTag("bytes", tag);
}
}
/**
* Reads a length
*
* <pre>
* <length>value</length>
* </pre>
*/
public int readLength()
throws IOException
{
int tag = parseTag();
if (tag != TAG_LENGTH) {
_peekTag = tag;
return -1;
}
int value = parseInt();
expectTag(TAG_LENGTH_END);
return value;
}
/**
* Reads a fault.
*/
private HashMap readFault()
throws IOException
{
HashMap map = new HashMap();
int code = parseTag();
for (; code >= 0 && code != TAG_FAULT_END; code = parseTag()) {
_peekTag = code;
Object key = readObject();
Object value = readObject();
if (key != null && value != null)
map.put(key, value);
}
if (code != TAG_FAULT_END)
throw expectedTag("fault", code);
return map;
}
/**
* Reads an object from the input stream with an expected type.
*/
public Object readObject(Class cl)
throws IOException
{
if (cl == null || cl.equals(Object.class))
return readObject();
int tag = parseTag();
switch (tag) {
case TAG_NULL:
expectTag(TAG_NULL_END);
return null;
case TAG_MAP:
{
String type = readType();
Deserializer reader;
reader = _serializerFactory.getObjectDeserializer(type, cl);
return reader.readMap(this);
}
case TAG_LIST:
{
String type = readType();
int length = readLength();
Deserializer reader;
reader = _serializerFactory.getObjectDeserializer(type, cl);
return reader.readList(this, length);
}
case TAG_REF:
{
int ref = parseInt();
expectTag(TAG_REF_END);
return _refs.get(ref);
}
case TAG_REMOTE:
{
String type = readType();
String url = readString();
expectTag(TAG_REMOTE_END);
Object remote = resolveRemote(type, url);
return remote;
}
}
_peekTag = tag;
Object value = _serializerFactory.getDeserializer(cl).readObject(this);
return value;
}
/**
* Reads an arbitrary object from the input stream when the type
* is unknown.
*/
public Object readObject()
throws IOException
{
int tag = parseTag();
switch (tag) {
case TAG_NULL:
expectTag(TAG_NULL_END);
return null;
case TAG_BOOLEAN:
{
int value = parseInt();
expectTag(TAG_BOOLEAN_END);
return new Boolean(value != 0);
}
case TAG_INT:
{
int value = parseInt();
expectTag(TAG_INT_END);
return new Integer(value);
}
case TAG_LONG:
{
long value = parseLong();
expectTag(TAG_LONG_END);
return new Long(value);
}
case TAG_DOUBLE:
{
double value = parseDouble();
expectTag(TAG_DOUBLE_END);
return new Double(value);
}
case TAG_DATE:
{
long value = parseDate();
expectTag(TAG_DATE_END);
return new Date(value);
}
case TAG_XML:
{
return parseXML();
}
case TAG_STRING:
{
_sbuf.setLength(0);
String value = parseString(_sbuf).toString();
expectTag(TAG_STRING_END);
return value;
}
case TAG_BASE64:
{
byte []data = parseBytes();
expectTag(TAG_BASE64_END);
return data;
}
case TAG_LIST:
{
String type = readType();
int length = readLength();
return _serializerFactory.readList(this, length, type);
}
case TAG_MAP:
{
String type = readType();
Deserializer deserializer;
deserializer = _serializerFactory.getObjectDeserializer(type);
return deserializer.readMap(this);
}
case TAG_REF:
{
int ref = parseInt();
expectTag(TAG_REF_END);
return _refs.get(ref);
}
case TAG_REMOTE:
{
String type = readType();
String url = readString();
expectTag(TAG_REMOTE_END);
return resolveRemote(type, url);
}
default:
throw error("unknown code:" + tagName(tag));
}
}
/**
* Reads a remote object.
*/
public Object readRemote()
throws IOException
{
String type = readType();
String url = readString();
return resolveRemote(type, url);
}
/**
* Reads a reference.
*/
public Object readRef()
throws IOException
{
return _refs.get(parseInt());
}
/**
* Reads the start of a list.
*/
public int readListStart()
throws IOException
{
return parseTag();
}
/**
* Reads the start of a map.
*/
public int readMapStart()
throws IOException
{
return parseTag();
}
/**
* Returns true if this is the end of a list or a map.
*/
public boolean isEnd()
throws IOException
{
int code = parseTag();
_peekTag = code;
return (code < 0 || code >= 100);
}
/**
* Reads the end byte.
*/
public void readEnd()
throws IOException
{
int code = parseTag();
if (code < 100)
throw error("unknown code:" + (char) code);
}
/**
* Reads the end of the map
*/
public void readMapEnd()
throws IOException
{
expectTag(TAG_MAP_END);
}
/**
* Reads the end of the map
*/
public void readListEnd()
throws IOException
{
expectTag(TAG_LIST_END);
}
/**
* Adds a list/map reference.
*/
public int addRef(Object ref)
{
if (_refs == null)
_refs = new ArrayList();
_refs.add(ref);
return _refs.size() - 1;
}
/**
* Adds a list/map reference.
*/
public void setRef(int i, Object ref)
{
_refs.set(i, ref);
}
/**
* Resolves a remote object.
*/
public Object resolveRemote(String type, String url)
throws IOException
{
HessianRemoteResolver resolver = getRemoteResolver();
if (resolver != null)
return resolver.lookup(type, url);
else
return new BurlapRemote(type, url);
}
/**
* Parses a type from the stream.
*
* <pre>
* <type>type</type>
* </pre>
*/
public String readType()
throws IOException
{
int code = parseTag();
if (code != TAG_TYPE) {
_peekTag = code;
return "";
}
_sbuf.setLength(0);
int ch;
while ((ch = readChar()) >= 0)
_sbuf.append((char) ch);
String type = _sbuf.toString();
expectTag(TAG_TYPE_END);
return type;
}
/**
* Parses a 32-bit integer value from the stream.
*/
private int parseInt()
throws IOException
{
int sign = 1;
int ch = read();
if (ch == '-') {
sign = -1;
ch = read();
}
int value = 0;
for (; ch >= '0' && ch <= '9'; ch = read())
value = 10 * value + ch - '0';
_peek = ch;
return sign * value;
}
/**
* Parses a 64-bit long value from the stream.
*/
private long parseLong()
throws IOException
{
int sign = 1;
int ch = read();
if (ch == '-') {
sign = -1;
ch = read();
}
long value = 0;
for (; ch >= '0' && ch <= '9'; ch = read())
value = 10 * value + ch - '0';
_peek = ch;
return sign * value;
}
/**
* Parses a 64-bit double value from the stream.
*
* <pre>
* b64 b56 b48 b40 b32 b24 b16 b8
* </pre>
*/
private double parseDouble()
throws IOException
{
int ch = skipWhitespace();
_sbuf.setLength(0);
for (; ! isWhitespace(ch) && ch != '<'; ch = read())
_sbuf.append((char) ch);
_peek = ch;
return new Double(_sbuf.toString()).doubleValue();
}
/**
* Parses a date value from the stream.
*/
protected long parseDate()
throws IOException
{
if (_utcCalendar == null)
_utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
return parseDate(_utcCalendar);
}
/**
* Parses a date value from the stream.
*/
protected long parseDate(Calendar calendar)
throws IOException
{
int ch = skipWhitespace();
int year = 0;
for (int i = 0; i < 4; i++) {
if (ch >= '0' && ch <= '9')
year = 10 * year + ch - '0';
else
throw expectedChar("year", ch);
ch = read();
}
int month = 0;
for (int i = 0; i < 2; i++) {
if (ch >= '0' && ch <= '9')
month = 10 * month + ch - '0';
else
throw expectedChar("month", ch);
ch = read();
}
int day = 0;
for (int i = 0; i < 2; i++) {
if (ch >= '0' && ch <= '9')
day = 10 * day + ch - '0';
else
throw expectedChar("day", ch);
ch = read();
}
if (ch != 'T')
throw expectedChar("`T'", ch);
ch = read();
int hour = 0;
for (int i = 0; i < 2; i++) {
if (ch >= '0' && ch <= '9')
hour = 10 * hour + ch - '0';
else
throw expectedChar("hour", ch);
ch = read();
}
int minute = 0;
for (int i = 0; i < 2; i++) {
if (ch >= '0' && ch <= '9')
minute = 10 * minute + ch - '0';
else
throw expectedChar("minute", ch);
ch = read();
}
int second = 0;
for (int i = 0; i < 2; i++) {
if (ch >= '0' && ch <= '9')
second = 10 * second + ch - '0';
else
throw expectedChar("second", ch);
ch = read();
}
int ms = 0;
if (ch == '.') {
ch = read();
while (ch >= '0' && ch <= '9') {
ms = 10 * ms + ch - '0';
ch = read();
}
}
for (; ch > 0 && ch != '<'; ch = read()) {
}
_peek = ch;
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, ms);
return calendar.getTime().getTime();
}
protected String parseString()
throws IOException
{
_sbuf.setLength(0);
return parseString(_sbuf).toString();
}
/**
* Parses a string value from the stream. The burlap object's
* string buffer is used for the result.
*/
protected StringBuffer parseString(StringBuffer sbuf)
throws IOException
{
int ch;
while ((ch = readChar()) >= 0)
sbuf.append((char) ch);
return sbuf;
}
org.w3c.dom.Node parseXML()
throws IOException
{
throw error("help!");
}
/**
* Reads a character from the underlying stream.
*/
int readChar()
throws IOException
{
int ch = read();
if (ch == '<' || ch < 0) {
_peek = ch;
return -1;
}
if (ch == '&') {
ch = read();
if (ch == '#') {
ch = read();
if (ch >= '0' && ch <= '9') {
int v = 0;
for (; ch >= '0' && ch <= '9'; ch = read()) {
v = 10 * v + ch - '0';
}
if (ch != ';')
throw error("expected ';' at " + (char) ch);
return (char) v;
}
else
throw error("expected digit at " + (char) ch);
}
else {
_entityBuffer.setLength(0);
for (; ch >= 'a' && ch <= 'z'; ch = read())
_entityBuffer.append((char) ch);
String entity = _entityBuffer.toString();
if (ch != ';')
throw expectedChar("';'", ch);
if (entity.equals("amp"))
return '&';
else if (entity.equals("apos"))
return '\'';
else if (entity.equals("quot"))
return '"';
else if (entity.equals("lt"))
return '<';
else if (entity.equals("gt"))
return '>';
else
throw new BurlapProtocolException("unknown XML entity &" + entity + "; at `" + (char) ch + "'");
}
}
else if (ch < 0x80)
return (char) ch;
else if ((ch & 0xe0) == 0xc0) {
int ch1 = read();
int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f);
return (char) v;
}
else if ((ch & 0xf0) == 0xe0) {
int ch1 = read();
int ch2 = read();
int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f);
return (char) v;
}
else
throw new BurlapProtocolException("bad utf-8 encoding");
}
/**
* Parses a byte array.
*/
protected byte []parseBytes()
throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
parseBytes(bos);
return bos.toByteArray();
}
/**
* Parses a byte array.
*/
protected ByteArrayOutputStream parseBytes(ByteArrayOutputStream bos)
throws IOException
{
int ch;
for (ch = skipWhitespace(); ch >= 0 && ch != '<'; ch = skipWhitespace()) {
int b1 = ch;
int b2 = read();
int b3 = read();
int b4 = read();
if (b4 != '=') {
int chunk = ((base64Decode[b1] << 18) +
(base64Decode[b2] << 12) +
(base64Decode[b3] << 6) +
(base64Decode[b4]));
bos.write(chunk >> 16);
bos.write(chunk >> 8);
bos.write(chunk);
}
else if (b3 != '=') {
int chunk = ((base64Decode[b1] << 10) +
(base64Decode[b2] << 4) +
(base64Decode[b3] >> 2));
bos.write(chunk >> 8);
bos.write(chunk);
}
else {
int chunk = ((base64Decode[b1] << 2) +
(base64Decode[b2] >> 4));
bos.write(chunk);
}
}
if (ch == '<')
_peek = ch;
return bos;
}
public void expectTag(int expectTag)
throws IOException
{
int tag = parseTag();
if (tag != expectTag)
throw error("expected " + tagName(expectTag) + " at " + tagName(tag));
}
/**
* Parses a tag. Returns true if it's a start tag.
*/
protected int parseTag()
throws IOException
{
if (_peekTag >= 0) {
int tag = _peekTag;
_peekTag = -1;
return tag;
}
int ch = skipWhitespace();
int endTagDelta = 0;
if (ch != '<')
throw expectedChar("'<'", ch);
ch = read();
if (ch == '/') {
endTagDelta = 100;
ch = _is.read();
}
if (! isTagChar(ch))
throw expectedChar("tag", ch);
_sbuf.setLength(0);
for (; isTagChar(ch); ch = read())
_sbuf.append((char) ch);
if (ch != '>')
throw expectedChar("'>'", ch);
Integer value = (Integer) _tagMap.get(_sbuf.toString());
if (value == null)
throw error("Unknown tag <" + _sbuf + ">");
return value.intValue() + endTagDelta;
}
/**
* Returns true if the character is a valid tag character.
*/
private boolean isTagChar(int ch)
{
return (ch >= 'a' && ch <= 'z' ||
ch >= 'A' && ch <= 'Z' ||
ch >= '0' && ch <= '9' ||
ch == ':' || ch == '-');
}
protected int skipWhitespace()
throws IOException
{
int ch = read();
for (;
ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
ch = read()) {
}
return ch;
}
protected boolean isWhitespace(int ch)
throws IOException
{
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}
/**
* Reads bytes from the underlying stream.
*/
int read(byte []buffer, int offset, int length)
throws IOException
{
throw new UnsupportedOperationException();
}
int read()
throws IOException
{
if (_peek >= 0) {
int value = _peek;
_peek = -1;
return value;
}
int ch = _is.read();
return ch;
}
public Reader getReader()
{
return null;
}
public InputStream readInputStream()
{
return null;
}
public InputStream getInputStream()
{
return null;
}
protected IOException expectBeginTag(String expect, String tag)
{
return new BurlapProtocolException("expected <" + expect + "> at <" + tag + ">");
}
protected IOException expectedChar(String expect, int ch)
{
if (ch < 0)
return error("expected " + expect + " at end of file");
else
return error("expected " + expect + " at " + (char) ch);
}
protected IOException expectedTag(String expect, int tag)
{
return error("expected " + expect + " at " + tagName(tag));
}
protected IOException error(String message)
{
return new BurlapProtocolException(message);
}
protected static String tagName(int tag)
{
switch (tag) {
case TAG_NULL:
return "<null>";
case TAG_NULL_END:
return "</null>";
case TAG_BOOLEAN:
return "<boolean>";
case TAG_BOOLEAN_END:
return "</boolean>";
case TAG_INT:
return "<int>";
case TAG_INT_END:
return "</int>";
case TAG_LONG:
return "<long>";
case TAG_LONG_END:
return "</long>";
case TAG_DOUBLE:
return "<double>";
case TAG_DOUBLE_END:
return "</double>";
case TAG_STRING:
return "<string>";
case TAG_STRING_END:
return "</string>";
case TAG_XML:
return "<xml>";
case TAG_XML_END:
return "</xml>";
case TAG_BASE64:
return "<base64>";
case TAG_BASE64_END:
return "</base64>";
case TAG_MAP:
return "<map>";
case TAG_MAP_END:
return "</map>";
case TAG_LIST:
return "<list>";
case TAG_LIST_END:
return "</list>";
case TAG_TYPE:
return "<type>";
case TAG_TYPE_END:
return "</type>";
case TAG_LENGTH:
return "<length>";
case TAG_LENGTH_END:
return "</length>";
case TAG_REF:
return "<ref>";
case TAG_REF_END:
return "</ref>";
case TAG_REMOTE:
return "<remote>";
case TAG_REMOTE_END:
return "</remote>";
case TAG_CALL:
return "<burlap:call>";
case TAG_CALL_END:
return "</burlap:call>";
case TAG_REPLY:
return "<burlap:reply>";
case TAG_REPLY_END:
return "</burlap:reply>";
case TAG_HEADER:
return "<header>";
case TAG_HEADER_END:
return "</header>";
case TAG_FAULT:
return "<fault>";
case TAG_FAULT_END:
return "</fault>";
case -1:
return "end of file";
default:
return "unknown " + tag;
}
}
static {
_tagMap = new HashMap();
_tagMap.put("null", new Integer(TAG_NULL));
_tagMap.put("boolean", new Integer(TAG_BOOLEAN));
_tagMap.put("int", new Integer(TAG_INT));
_tagMap.put("long", new Integer(TAG_LONG));
_tagMap.put("double", new Integer(TAG_DOUBLE));
_tagMap.put("date", new Integer(TAG_DATE));
_tagMap.put("string", new Integer(TAG_STRING));
_tagMap.put("xml", new Integer(TAG_XML));
_tagMap.put("base64", new Integer(TAG_BASE64));
_tagMap.put("map", new Integer(TAG_MAP));
_tagMap.put("list", new Integer(TAG_LIST));
_tagMap.put("type", new Integer(TAG_TYPE));
_tagMap.put("length", new Integer(TAG_LENGTH));
_tagMap.put("ref", new Integer(TAG_REF));
_tagMap.put("remote", new Integer(TAG_REMOTE));
_tagMap.put("burlap:call", new Integer(TAG_CALL));
_tagMap.put("burlap:reply", new Integer(TAG_REPLY));
_tagMap.put("fault", new Integer(TAG_FAULT));
_tagMap.put("method", new Integer(TAG_METHOD));
_tagMap.put("header", new Integer(TAG_HEADER));
}
static {
base64Decode = new int[256];
for (int i = 'A'; i <= 'Z'; i++)
base64Decode[i] = i - 'A';
for (int i = 'a'; i <= 'z'; i++)
base64Decode[i] = i - 'a' + 26;
for (int i = '0'; i <= '9'; i++)
base64Decode[i] = i - '0' + 52;
base64Decode['+'] = 62;
base64Decode['/'] = 63;
}
static {
try {
_detailMessageField = Throwable.class.getDeclaredField("detailMessage");
_detailMessageField.setAccessible(true);
} catch (Throwable e) {
}
}
}