/*
 * 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.

BurlapInput is unbuffered, so any client needs to provide its own buffering.

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
/** * 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.
/** * Creates an uninitialized Burlap input stream. */
public BurlapInput() { }
Creates a new Burlap input stream, initialized with an underlying input stream.
Params:
  • is – the underlying input stream.
/** * 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.
/** * Sets the serializer factory. */
public void setSerializerFactory(SerializerFactory factory) { _serializerFactory = factory; }
Gets the serializer factory.
/** * Gets the serializer factory. */
public SerializerFactory getSerializerFactory() { return _serializerFactory; }
Initialize the burlap stream with the underlying input stream.
/** * 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
/** * Returns the calls method */
public String getMethod() { return _method; }
Returns any reply fault.
/** * Returns any reply fault. */
public Throwable getReplyFault() { return _replyFault; }
Starts reading the call
<burlap:call>
<method>method</method>
/** * Starts reading the call * * <pre> * &lt;burlap:call> * &lt;method>method&lt;/method> * </pre> */
public void startCall() throws IOException { readCall(); while ((readHeader() != null)) readObject(); readMethod(); }
Starts reading the call

A successful completion will have a single value:

<burlap:call>
/** * Starts reading the call * * <p>A successful completion will have a single value: * * <pre> * &lt;burlap:call> * </pre> */
public int readCall() throws IOException { expectTag(TAG_CALL); int major = 1; int minor = 0; return (major << 16) + minor; }
Reads the method
<method>method</method>
/** * Reads the method * * <pre> * &lt;method>method&lt;/method> * </pre> */
public String readMethod() throws IOException { expectTag(TAG_METHOD); _method = parseString(); expectTag(TAG_METHOD_END); return _method; }
Completes reading the call

A successful completion will have a single value:

</burlap:call>
/** * Completes reading the call * * <p>A successful completion will have a single value: * * <pre> * &lt;/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.
/** * 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

A successful completion will have a single value:

<burlap:reply>
<value>
/** * Starts reading the reply * * <p>A successful completion will have a single value: * * <pre> * &lt;burlap:reply> * &lt;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.
/** * 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; Field detailMessageField = getDetailMessageField(); 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

A successful completion will have a single value:

</burlap:reply>
/** * Completes reading the call * * <p>A successful completion will have a single value: * * <pre> * &lt;/burlap:reply> * </pre> */
public void completeReply() throws IOException { expectTag(TAG_REPLY_END); }
Reads a header, returning null if there are no headers.
<header>value</header>
/** * Reads a header, returning null if there are no headers. * * <pre> * &lt;header>value&lt;/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
<null></null>
/** * Reads a null * * <pre> * &lt;null>&lt;/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
<boolean>0</boolean>
<boolean>1</boolean>
/** * Reads a boolean * * <pre> * &lt;boolean>0&lt;/boolean> * &lt;boolean>1&lt;/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
<int>value</int>
/** * Reads a byte * * <pre> * &lt;int>value&lt;/int> * </pre> */
public byte readByte() throws IOException { return (byte) readInt(); }
Reads a short
<int>value</int>
/** * Reads a short * * <pre> * &lt;int>value&lt;/int> * </pre> */
public short readShort() throws IOException { return (short) readInt(); }
Reads an integer
<int>value</int>
/** * Reads an integer * * <pre> * &lt;int>value&lt;/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
<long>value</long>
/** * Reads a long * * <pre> * &lt;long>value&lt;/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
<double>value</double>
/** * Reads a float * * <pre> * &lt;double>value&lt;/double> * </pre> */
public float readFloat() throws IOException { return (float) readDouble(); }
Reads a double
<double>value</double>
/** * Reads a double * * <pre> * &lt;double>value&lt;/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.
<date>ISO-8609 date</date>
/** * Reads a date. * * <pre> * &lt;date>ISO-8609 date&lt;/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.
<date>ISO-8609 date</date>
/** * Reads a date. * * <pre> * &lt;date>ISO-8609 date&lt;/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
<string>value</string>
/** * Reads a string * * <pre> * &lt;string>value&lt;/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.
&xml;xml string</xml>
/** * Reads an XML node. * * <pre> * &xml;xml string&lt;/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
<base64>...</base64>
/** * Reads a byte array * * <pre> * &lt;base64>...&lt;/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
<length>value</length>
/** * Reads a length * * <pre> * &lt;length>value&lt;/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.
/** * 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.
/** * 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.
/** * 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.
/** * Reads a remote object. */
public Object readRemote() throws IOException { String type = readType(); String url = readString(); return resolveRemote(type, url); }
Reads a reference.
/** * Reads a reference. */
public Object readRef() throws IOException { return _refs.get(parseInt()); }
Reads the start of a list.
/** * Reads the start of a list. */
public int readListStart() throws IOException { return parseTag(); }
Reads the start of a map.
/** * 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.
/** * 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.
/** * 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
/** * Reads the end of the map */
public void readMapEnd() throws IOException { expectTag(TAG_MAP_END); }
Reads the end of the map
/** * Reads the end of the map */
public void readListEnd() throws IOException { expectTag(TAG_LIST_END); }
Adds a list/map reference.
/** * 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.
/** * Adds a list/map reference. */
public void setRef(int i, Object ref) { _refs.set(i, ref); }
Resolves a remote object.
/** * 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.
<type>type</type>
/** * Parses a type from the stream. * * <pre> * &lt;type>type&lt;/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.
/** * 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.
/** * 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.
b64 b56 b48 b40 b32 b24 b16 b8
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Parses a byte array. */
protected byte []parseBytes() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); parseBytes(bos); return bos.toByteArray(); }
Parses a byte array.
/** * 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.
/** * 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.
/** * 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.
/** * 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; } private static Field getDetailMessageField() { if (_detailMessageField == null) { try { _detailMessageField = Throwable.class.getDeclaredField("detailMessage"); _detailMessageField.setAccessible(true); } catch (Throwable e) { } } return _detailMessageField; } }