| /* |
| * 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.alibaba.com.caucho.hessian.io; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.lang.reflect.Field; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Input stream for Hessian requests. |
| * <p> |
| * <p>HessianInput is unbuffered, so any client needs to provide |
| * its own buffering. |
| * <p> |
| * <pre> |
| * InputStream is = ...; // from http connection |
| * HessianInput in = new HessianInput(is); |
| * String value; |
| * |
| * in.startReply(); // read reply header |
| * value = in.readString(); // read string value |
| * in.completeReply(); // read reply footer |
| * </pre> |
| */ |
| public class Hessian2Input |
| extends AbstractHessianInput |
| implements Hessian2Constants { |
| private static final Logger log |
| = Logger.getLogger(Hessian2Input.class.getName()); |
| |
| private static final double D_256 = 1.0 / 256.0; |
| private static final int END_OF_DATA = -2; |
| private static final int SIZE = 256; |
| private static final int GAP = 16; |
| private static Field _detailMessageField; |
| private static boolean _isCloseStreamOnClose; |
| public static boolean _isNull = false; |
| |
| static { |
| try { |
| _detailMessageField = Throwable.class.getDeclaredField("detailMessage"); |
| _detailMessageField.setAccessible(true); |
| } catch (Throwable e) { |
| } |
| } |
| |
| private final byte[] _buffer = new byte[SIZE]; |
| // factory for deserializing objects in the input stream |
| protected SerializerFactory _serializerFactory; |
| protected ArrayList _refs; |
| protected ArrayList _classDefs; |
| protected ArrayList _types; |
| // the underlying input stream |
| private InputStream _is; |
| // a peek character |
| private int _offset; |
| private int _length; |
| // true for streaming data |
| private boolean _isStreaming; |
| // the method for a call |
| private String _method; |
| private int _argLength; |
| private Reader _chunkReader; |
| private InputStream _chunkInputStream; |
| private Throwable _replyFault; |
| private StringBuilder _sbuf = new StringBuilder(); |
| // true if this is the last chunk |
| private boolean _isLastChunk; |
| // the chunk length |
| private int _chunkLength; |
| |
| /** |
| * Creates a new Hessian input stream, initialized with an |
| * underlying input stream. |
| * |
| * @param is the underlying input stream. |
| */ |
| public Hessian2Input(InputStream is) { |
| _is = is; |
| } |
| |
| /** |
| * Gets the serializer factory. |
| */ |
| public SerializerFactory getSerializerFactory() { |
| return _serializerFactory; |
| } |
| |
| /** |
| * Sets the serializer factory. |
| */ |
| @Override |
| public void setSerializerFactory(SerializerFactory factory) { |
| _serializerFactory = factory; |
| } |
| |
| /** |
| * Gets the serializer factory, creating a default if necessary. |
| */ |
| public final SerializerFactory findSerializerFactory() { |
| SerializerFactory factory = _serializerFactory; |
| |
| if (factory == null) |
| _serializerFactory = factory = new SerializerFactory(); |
| |
| return factory; |
| } |
| |
| public boolean isCloseStreamOnClose() { |
| return _isCloseStreamOnClose; |
| } |
| |
| public void setCloseStreamOnClose(boolean isClose) { |
| _isCloseStreamOnClose = isClose; |
| } |
| |
| /** |
| * Returns the calls method |
| */ |
| @Override |
| public String getMethod() { |
| return _method; |
| } |
| |
| /** |
| * Returns any reply fault. |
| */ |
| public Throwable getReplyFault() { |
| return _replyFault; |
| } |
| |
| /** |
| * Starts reading the call |
| * <p> |
| * <pre> |
| * c major minor |
| * </pre> |
| */ |
| @Override |
| public int readCall() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag != 'C') |
| throw error("expected hessian call ('C') at " + codeName(tag)); |
| |
| return 0; |
| } |
| |
| /** |
| * Starts reading the envelope |
| * <p> |
| * <pre> |
| * E major minor |
| * </pre> |
| */ |
| public int readEnvelope() |
| throws IOException { |
| int tag = read(); |
| int version = 0; |
| |
| if (tag == 'H') { |
| int major = read(); |
| int minor = read(); |
| |
| version = (major << 16) + minor; |
| |
| tag = read(); |
| } |
| |
| if (tag != 'E') |
| throw error("expected hessian Envelope ('E') at " + codeName(tag)); |
| |
| return version; |
| } |
| |
| /** |
| * Completes reading the envelope |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * Z |
| * </pre> |
| */ |
| public void completeEnvelope() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag != 'Z') |
| error("expected end of envelope at " + codeName(tag)); |
| } |
| |
| /** |
| * Starts reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * string |
| * </pre> |
| */ |
| @Override |
| public String readMethod() |
| throws IOException { |
| _method = readString(); |
| |
| return _method; |
| } |
| |
| /** |
| * Returns the number of method arguments |
| * <p> |
| * <pre> |
| * int |
| * </pre> |
| */ |
| @Override |
| public int readMethodArgLength() |
| throws IOException { |
| return readInt(); |
| } |
| |
| /** |
| * Starts reading the call, including the headers. |
| * <p> |
| * <p>The call expects the following protocol data |
| * <p> |
| * <pre> |
| * c major minor |
| * m b16 b8 method |
| * </pre> |
| */ |
| @Override |
| public void startCall() |
| throws IOException { |
| readCall(); |
| |
| readMethod(); |
| } |
| |
| /** |
| * Completes reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * </pre> |
| */ |
| @Override |
| public void completeCall() |
| throws IOException { |
| } |
| |
| /** |
| * Reads a reply as an object. |
| * If the reply has a fault, throws the exception. |
| */ |
| @Override |
| public Object readReply(Class expectedClass) |
| throws Throwable { |
| int tag = read(); |
| |
| if (tag == 'R') |
| return readObject(expectedClass); |
| else if (tag == 'F') { |
| HashMap map = (HashMap) readObject(HashMap.class); |
| |
| throw prepareFault(map); |
| } else { |
| StringBuilder sb = new StringBuilder(); |
| sb.append((char) tag); |
| |
| try { |
| int ch; |
| |
| while ((ch = read()) >= 0) { |
| sb.append((char) ch); |
| } |
| } catch (IOException e) { |
| log.log(Level.FINE, e.toString(), e); |
| } |
| |
| throw error("expected hessian reply at " + codeName(tag) + "\n" |
| + sb); |
| } |
| } |
| |
| /** |
| * Starts reading the reply |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * r |
| * </pre> |
| */ |
| @Override |
| public void startReply() |
| throws Throwable { |
| // XXX: for variable length (?) |
| |
| readReply(Object.class); |
| } |
| |
| /** |
| * Prepares the fault. |
| */ |
| private Throwable prepareFault(HashMap fault) |
| throws IOException { |
| 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 HessianServiceException(message, code, detail); |
| |
| return _replyFault; |
| } |
| } |
| |
| /** |
| * Completes reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * z |
| * </pre> |
| */ |
| @Override |
| public void completeReply() |
| throws IOException { |
| } |
| |
| /** |
| * Completes reading the call |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * z |
| * </pre> |
| */ |
| public void completeValueReply() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag != 'Z') |
| error("expected end of reply at " + codeName(tag)); |
| } |
| |
| /** |
| * Reads a header, returning null if there are no headers. |
| * <p> |
| * <pre> |
| * H b16 b8 value |
| * </pre> |
| */ |
| @Override |
| public String readHeader() |
| throws IOException { |
| return null; |
| } |
| |
| /** |
| * Starts reading the message |
| * <p> |
| * <pre> |
| * p major minor |
| * </pre> |
| */ |
| public int startMessage() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag == 'p') |
| _isStreaming = false; |
| else if (tag == 'P') |
| _isStreaming = true; |
| else |
| throw error("expected Hessian message ('p') at " + codeName(tag)); |
| |
| int major = read(); |
| int minor = read(); |
| |
| return (major << 16) + minor; |
| } |
| |
| /** |
| * Completes reading the message |
| * <p> |
| * <p>A successful completion will have a single value: |
| * <p> |
| * <pre> |
| * z |
| * </pre> |
| */ |
| public void completeMessage() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag != 'Z') |
| error("expected end of message at " + codeName(tag)); |
| } |
| |
| /** |
| * Reads a null |
| * <p> |
| * <pre> |
| * N |
| * </pre> |
| */ |
| @Override |
| public void readNull() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return; |
| |
| default: |
| throw expect("null", tag); |
| } |
| } |
| |
| /** |
| * Reads a boolean |
| * <p> |
| * <pre> |
| * T |
| * F |
| * </pre> |
| */ |
| @Override |
| public boolean readBoolean() |
| throws IOException { |
| int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| switch (tag) { |
| case 'T': |
| return true; |
| case 'F': |
| return false; |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return tag != BC_INT_ZERO; |
| |
| // INT_BYTE = 0 |
| case 0xc8: |
| return read() != 0; |
| |
| // INT_BYTE != 0 |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| read(); |
| return true; |
| |
| // INT_SHORT = 0 |
| case 0xd4: |
| return (256 * read() + read()) != 0; |
| |
| // INT_SHORT != 0 |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| read(); |
| read(); |
| return true; |
| |
| case 'I': |
| return |
| parseInt() != 0; |
| |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return tag != BC_LONG_ZERO; |
| |
| // LONG_BYTE = 0 |
| case 0xf8: |
| return read() != 0; |
| |
| // LONG_BYTE != 0 |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| read(); |
| return true; |
| |
| // INT_SHORT = 0 |
| case 0x3c: |
| return (256 * read() + read()) != 0; |
| |
| // INT_SHORT != 0 |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| read(); |
| read(); |
| return true; |
| |
| case BC_LONG_INT: |
| return (0x1000000L * read() |
| + 0x10000L * read() |
| + 0x100 * read() |
| + read()) != 0; |
| |
| case 'L': |
| return parseLong() != 0; |
| |
| case BC_DOUBLE_ZERO: |
| return false; |
| |
| case BC_DOUBLE_ONE: |
| return true; |
| |
| case BC_DOUBLE_BYTE: |
| return read() != 0; |
| |
| case BC_DOUBLE_SHORT: |
| return (0x100 * read() + read()) != 0; |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return mills != 0; |
| } |
| |
| case 'D': |
| return parseDouble() != 0.0; |
| |
| case 'N': |
| return false; |
| |
| default: |
| throw expect("boolean", tag); |
| } |
| } |
| |
| /** |
| * Reads a short |
| * <p> |
| * <pre> |
| * I b32 b24 b16 b8 |
| * </pre> |
| */ |
| public short readShort() |
| throws IOException { |
| return (short) readInt(); |
| } |
| |
| /** |
| * Reads an integer |
| * <p> |
| * <pre> |
| * I b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public final int readInt() |
| throws IOException { |
| //int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| _isNull = false; |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| _isNull = true; |
| return 0; |
| |
| case 'F': |
| return 0; |
| |
| case 'T': |
| return 1; |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return tag - BC_INT_ZERO; |
| |
| /* byte int */ |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc8: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); |
| |
| /* short int */ |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd4: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| case 'I': |
| case BC_LONG_INT: |
| return ((read() << 24) |
| + (read() << 16) |
| + (read() << 8) |
| + read()); |
| |
| // direct long |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return tag - BC_LONG_ZERO; |
| |
| /* byte long */ |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf8: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); |
| |
| /* short long */ |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| case 'L': |
| return (int) parseLong(); |
| |
| case BC_DOUBLE_ZERO: |
| return 0; |
| |
| case BC_DOUBLE_ONE: |
| return 1; |
| |
| //case LONG_BYTE: |
| case BC_DOUBLE_BYTE: |
| return (byte) (_offset < _length ? _buffer[_offset++] : read()); |
| |
| //case INT_SHORT: |
| //case LONG_SHORT: |
| case BC_DOUBLE_SHORT: |
| return (short) (256 * read() + read()); |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return (int) (0.001 * mills); |
| } |
| |
| case 'D': |
| return (int) parseDouble(); |
| |
| default: |
| throw expect("integer", tag); |
| } |
| } |
| |
| /** |
| * Reads a long |
| * <p> |
| * <pre> |
| * L b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public long readLong() |
| throws IOException { |
| _isNull = false; |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| _isNull = true; |
| return 0; |
| |
| case 'F': |
| return 0; |
| |
| case 'T': |
| return 1; |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return tag - BC_INT_ZERO; |
| |
| /* byte int */ |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc8: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); |
| |
| /* short int */ |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd4: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| //case LONG_BYTE: |
| case BC_DOUBLE_BYTE: |
| return (byte) (_offset < _length ? _buffer[_offset++] : read()); |
| |
| //case INT_SHORT: |
| //case LONG_SHORT: |
| case BC_DOUBLE_SHORT: |
| return (short) (256 * read() + read()); |
| |
| case 'I': |
| case BC_LONG_INT: |
| return parseInt(); |
| |
| // direct long |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return tag - BC_LONG_ZERO; |
| |
| /* byte long */ |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf8: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); |
| |
| /* short long */ |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| case 'L': |
| return parseLong(); |
| |
| case BC_DOUBLE_ZERO: |
| return 0; |
| |
| case BC_DOUBLE_ONE: |
| return 1; |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return (long) (0.001 * mills); |
| } |
| |
| case 'D': |
| return (long) parseDouble(); |
| |
| default: |
| throw expect("long", tag); |
| } |
| } |
| |
| /** |
| * Reads a float |
| * <p> |
| * <pre> |
| * D b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| public float readFloat() |
| throws IOException { |
| return (float) readDouble(); |
| } |
| |
| /** |
| * Reads a double |
| * <p> |
| * <pre> |
| * D b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public double readDouble() |
| throws IOException { |
| _isNull = false; |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| _isNull = true; |
| return 0; |
| |
| case 'F': |
| return 0; |
| |
| case 'T': |
| return 1; |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return tag - 0x90; |
| |
| /* byte int */ |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc8: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); |
| |
| /* short int */ |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd4: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| case 'I': |
| case BC_LONG_INT: |
| return parseInt(); |
| |
| // direct long |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return tag - BC_LONG_ZERO; |
| |
| /* byte long */ |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf8: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); |
| |
| /* short long */ |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); |
| |
| case 'L': |
| return (double) parseLong(); |
| |
| case BC_DOUBLE_ZERO: |
| return 0; |
| |
| case BC_DOUBLE_ONE: |
| return 1; |
| |
| case BC_DOUBLE_BYTE: |
| return (byte) (_offset < _length ? _buffer[_offset++] : read()); |
| |
| case BC_DOUBLE_SHORT: |
| return (short) (256 * read() + read()); |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return 0.001 * mills; |
| } |
| |
| case 'D': |
| return parseDouble(); |
| |
| default: |
| throw expect("double", tag); |
| } |
| } |
| |
| /** |
| * Reads a date. |
| * <p> |
| * <pre> |
| * T b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public long readUTCDate() |
| throws IOException { |
| int tag = read(); |
| |
| if (tag == BC_DATE) { |
| return parseLong(); |
| } else if (tag == BC_DATE_MINUTE) { |
| return parseInt() * 60000L; |
| } else |
| throw expect("date", tag); |
| } |
| |
| /** |
| * Reads a byte from the stream. |
| */ |
| public int readChar() |
| throws IOException { |
| if (_chunkLength > 0) { |
| _chunkLength--; |
| if (_chunkLength == 0 && _isLastChunk) |
| _chunkLength = END_OF_DATA; |
| |
| int ch = parseUTF8Char(); |
| return ch; |
| } else if (_chunkLength == END_OF_DATA) { |
| _chunkLength = 0; |
| return -1; |
| } |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return -1; |
| |
| case 'S': |
| case BC_STRING_CHUNK: |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| |
| _chunkLength--; |
| int value = parseUTF8Char(); |
| |
| // special code so successive read byte won't |
| // be read as a single object. |
| if (_chunkLength == 0 && _isLastChunk) |
| _chunkLength = END_OF_DATA; |
| |
| return value; |
| |
| default: |
| throw expect("char", tag); |
| } |
| } |
| |
| /** |
| * Reads a byte array from the stream. |
| */ |
| public int readString(char[] buffer, int offset, int length) |
| throws IOException { |
| int readLength = 0; |
| |
| if (_chunkLength == END_OF_DATA) { |
| _chunkLength = 0; |
| return -1; |
| } else if (_chunkLength == 0) { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return -1; |
| |
| case 'S': |
| case BC_STRING_CHUNK: |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 0x00: |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| case 0x04: |
| case 0x05: |
| case 0x06: |
| case 0x07: |
| case 0x08: |
| case 0x09: |
| case 0x0a: |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| |
| case 0x10: |
| case 0x11: |
| case 0x12: |
| case 0x13: |
| case 0x14: |
| case 0x15: |
| case 0x16: |
| case 0x17: |
| case 0x18: |
| case 0x19: |
| case 0x1a: |
| case 0x1b: |
| case 0x1c: |
| case 0x1d: |
| case 0x1e: |
| case 0x1f: |
| _isLastChunk = true; |
| _chunkLength = tag - 0x00; |
| break; |
| |
| default: |
| throw expect("string", tag); |
| } |
| } |
| |
| while (length > 0) { |
| if (_chunkLength > 0) { |
| buffer[offset++] = (char) parseUTF8Char(); |
| _chunkLength--; |
| length--; |
| readLength++; |
| } else if (_isLastChunk) { |
| if (readLength == 0) |
| return -1; |
| else { |
| _chunkLength = END_OF_DATA; |
| return readLength; |
| } |
| } else { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'S': |
| case BC_STRING_CHUNK: |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw expect("string", tag); |
| } |
| } |
| } |
| |
| if (readLength == 0) |
| return -1; |
| else if (_chunkLength > 0 || !_isLastChunk) |
| return readLength; |
| else { |
| _chunkLength = END_OF_DATA; |
| return readLength; |
| } |
| } |
| |
| /** |
| * Reads a string |
| * <p> |
| * <pre> |
| * S b16 b8 string value |
| * </pre> |
| */ |
| @Override |
| public String readString() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| case 'T': |
| return "true"; |
| case 'F': |
| return "false"; |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return String.valueOf((tag - 0x90)); |
| |
| /* byte int */ |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc8: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| return String.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read()); |
| |
| /* short int */ |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd4: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| return String.valueOf(((tag - BC_INT_SHORT_ZERO) << 16) |
| + 256 * read() + read()); |
| |
| case 'I': |
| case BC_LONG_INT: |
| return String.valueOf(parseInt()); |
| |
| // direct long |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return String.valueOf(tag - BC_LONG_ZERO); |
| |
| /* byte long */ |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf8: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| return String.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read()); |
| |
| /* short long */ |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| return String.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16) |
| + 256 * read() + read()); |
| |
| case 'L': |
| return String.valueOf(parseLong()); |
| |
| case BC_DOUBLE_ZERO: |
| return "0.0"; |
| |
| case BC_DOUBLE_ONE: |
| return "1.0"; |
| |
| case BC_DOUBLE_BYTE: |
| return String.valueOf((byte) (_offset < _length |
| ? _buffer[_offset++] |
| : read())); |
| |
| case BC_DOUBLE_SHORT: |
| return String.valueOf(((short) (256 * read() + read()))); |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return String.valueOf(0.001 * mills); |
| } |
| |
| case 'D': |
| return String.valueOf(parseDouble()); |
| |
| case 'S': |
| case BC_STRING_CHUNK: |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| |
| _sbuf.setLength(0); |
| int ch; |
| |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| |
| // 0-byte string |
| case 0x00: |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| case 0x04: |
| case 0x05: |
| case 0x06: |
| case 0x07: |
| case 0x08: |
| case 0x09: |
| case 0x0a: |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| |
| case 0x10: |
| case 0x11: |
| case 0x12: |
| case 0x13: |
| case 0x14: |
| case 0x15: |
| case 0x16: |
| case 0x17: |
| case 0x18: |
| case 0x19: |
| case 0x1a: |
| case 0x1b: |
| case 0x1c: |
| case 0x1d: |
| case 0x1e: |
| case 0x1f: |
| _isLastChunk = true; |
| _chunkLength = tag - 0x00; |
| |
| _sbuf.setLength(0); |
| |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| |
| case 0x30: |
| case 0x31: |
| case 0x32: |
| case 0x33: |
| _isLastChunk = true; |
| _chunkLength = (tag - 0x30) * 256 + read(); |
| |
| _sbuf.setLength(0); |
| |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| |
| default: |
| throw expect("string", tag); |
| } |
| } |
| |
| /** |
| * Reads a byte array |
| * <p> |
| * <pre> |
| * B b16 b8 data value |
| * </pre> |
| */ |
| @Override |
| public byte[] readBytes() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'B': |
| case BC_BINARY_CHUNK: |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| int data; |
| while ((data = parseByte()) >= 0) |
| bos.write(data); |
| |
| return bos.toByteArray(); |
| |
| case 0x20: |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| case 0x25: |
| case 0x26: |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2d: |
| case 0x2e: |
| case 0x2f: { |
| _isLastChunk = true; |
| _chunkLength = tag - 0x20; |
| |
| byte[] buffer = new byte[_chunkLength]; |
| |
| int k = 0; |
| while ((data = parseByte()) >= 0) |
| buffer[k++] = (byte) data; |
| |
| return buffer; |
| } |
| |
| case 0x34: |
| case 0x35: |
| case 0x36: |
| case 0x37: { |
| _isLastChunk = true; |
| _chunkLength = (tag - 0x34) * 256 + read(); |
| |
| byte[] buffer = new byte[_chunkLength]; |
| int k = 0; |
| |
| while ((data = parseByte()) >= 0) { |
| buffer[k++] = (byte) data; |
| } |
| |
| return buffer; |
| } |
| |
| default: |
| throw expect("bytes", tag); |
| } |
| } |
| |
| /** |
| * Reads a byte from the stream. |
| */ |
| public int readByte() |
| throws IOException { |
| if (_chunkLength > 0) { |
| _chunkLength--; |
| if (_chunkLength == 0 && _isLastChunk) |
| _chunkLength = END_OF_DATA; |
| |
| return read(); |
| } else if (_chunkLength == END_OF_DATA) { |
| _chunkLength = 0; |
| return -1; |
| } |
| |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return -1; |
| |
| case 'B': |
| case BC_BINARY_CHUNK: |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| |
| int value = parseByte(); |
| |
| // special code so successive read byte won't |
| // be read as a single object. |
| if (_chunkLength == 0 && _isLastChunk) |
| _chunkLength = END_OF_DATA; |
| |
| return value; |
| |
| default: |
| throw expect("binary", tag); |
| } |
| } |
| |
| /** |
| * Reads a byte array from the stream. |
| */ |
| public int readBytes(byte[] buffer, int offset, int length) |
| throws IOException { |
| int readLength = 0; |
| |
| if (_chunkLength == END_OF_DATA) { |
| _chunkLength = 0; |
| return -1; |
| } else if (_chunkLength == 0) { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return -1; |
| |
| case 'B': |
| case BC_BINARY_CHUNK: |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw expect("binary", tag); |
| } |
| } |
| |
| while (length > 0) { |
| if (_chunkLength > 0) { |
| buffer[offset++] = (byte) read(); |
| _chunkLength--; |
| length--; |
| readLength++; |
| } else if (_isLastChunk) { |
| if (readLength == 0) |
| return -1; |
| else { |
| _chunkLength = END_OF_DATA; |
| return readLength; |
| } |
| } else { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'B': |
| case BC_BINARY_CHUNK: |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| default: |
| throw expect("binary", tag); |
| } |
| } |
| } |
| |
| if (readLength == 0) |
| return -1; |
| else if (_chunkLength > 0 || !_isLastChunk) |
| return readLength; |
| else { |
| _chunkLength = END_OF_DATA; |
| return readLength; |
| } |
| } |
| |
| /** |
| * Reads a fault. |
| */ |
| private HashMap readFault() |
| throws IOException { |
| HashMap map = new HashMap(); |
| |
| int code = read(); |
| for (; code > 0 && code != 'Z'; code = read()) { |
| _offset--; |
| |
| Object key = readObject(); |
| Object value = readObject(); |
| |
| if (key != null && value != null) |
| map.put(key, value); |
| } |
| |
| if (code != 'Z') |
| throw expect("fault", code); |
| |
| return map; |
| } |
| |
| /** |
| * Reads an object from the input stream with an expected type. |
| */ |
| @Override |
| public Object readObject(Class cl) |
| throws IOException { |
| return readObject(cl, null, null); |
| } |
| |
| @Override |
| public Object readObject(Class expectedClass, Class<?>... expectedTypes) throws IOException { |
| if (expectedClass == null || expectedClass == Object.class) |
| return readObject(); |
| |
| int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'H': { |
| Deserializer reader = findSerializerFactory().getDeserializer(expectedClass); |
| |
| boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2; |
| // fix deserialize of short type |
| return reader.readMap(this |
| , keyValuePair ? expectedTypes[0] : null |
| , keyValuePair ? expectedTypes[1] : null); |
| } |
| |
| case 'M': { |
| String type = readType(); |
| |
| // hessian/3bb3 |
| if ("".equals(type)) { |
| Deserializer reader; |
| reader = findSerializerFactory().getDeserializer(expectedClass); |
| |
| return reader.readMap(this); |
| } else { |
| Deserializer reader; |
| reader = findSerializerFactory().getObjectDeserializer(type, expectedClass); |
| |
| return reader.readMap(this); |
| } |
| } |
| |
| case 'C': { |
| readObjectDefinition(expectedClass); |
| |
| return readObject(expectedClass); |
| } |
| |
| case 0x60: |
| case 0x61: |
| case 0x62: |
| case 0x63: |
| case 0x64: |
| case 0x65: |
| case 0x66: |
| case 0x67: |
| case 0x68: |
| case 0x69: |
| case 0x6a: |
| case 0x6b: |
| case 0x6c: |
| case 0x6d: |
| case 0x6e: |
| case 0x6f: { |
| int ref = tag - 0x60; |
| int size = _classDefs.size(); |
| |
| if (ref < 0 || size <= ref) |
| throw new HessianProtocolException("'" + ref + "' is an unknown class definition"); |
| |
| ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref); |
| |
| return readObjectInstance(expectedClass, def); |
| } |
| |
| case 'O': { |
| int ref = readInt(); |
| int size = _classDefs.size(); |
| |
| if (ref < 0 || size <= ref) |
| throw new HessianProtocolException("'" + ref + "' is an unknown class definition"); |
| |
| ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref); |
| |
| return readObjectInstance(expectedClass, def); |
| } |
| |
| case BC_LIST_VARIABLE: { |
| String type = readType(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(type, expectedClass); |
| |
| Object v = reader.readList(this, -1); |
| |
| return v; |
| } |
| |
| case BC_LIST_FIXED: { |
| String type = readType(); |
| int length = readInt(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(type, expectedClass); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case 0x70: |
| case 0x71: |
| case 0x72: |
| case 0x73: |
| case 0x74: |
| case 0x75: |
| case 0x76: |
| case 0x77: { |
| int length = tag - 0x70; |
| |
| String type = readType(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, expectedClass); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| // fix deserialize of short type |
| Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case BC_LIST_VARIABLE_UNTYPED: { |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, expectedClass); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| // fix deserialize of short type |
| Object v = reader.readList(this, -1, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case BC_LIST_FIXED_UNTYPED: { |
| int length = readInt(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, expectedClass); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| // fix deserialize of short type |
| Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case 0x78: |
| case 0x79: |
| case 0x7a: |
| case 0x7b: |
| case 0x7c: |
| case 0x7d: |
| case 0x7e: |
| case 0x7f: { |
| int length = tag - 0x78; |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, expectedClass); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.length == 1; |
| |
| // fix deserialize of short type |
| Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null); |
| |
| return v; |
| } |
| |
| case BC_REF: { |
| int ref = readInt(); |
| |
| return _refs.get(ref); |
| } |
| } |
| |
| if (tag >= 0) |
| _offset--; |
| |
| // hessian/3b2i vs hessian/3406 |
| // return readObject(); |
| Object value = findSerializerFactory().getDeserializer(expectedClass).readObject(this); |
| return value; |
| } |
| |
| /** |
| * Reads an arbitrary object from the input stream when the type |
| * is unknown. |
| */ |
| @Override |
| public Object readObject() |
| throws IOException { |
| return readObject((List<Class<?>>) null); |
| } |
| |
| @Override |
| public Object readObject(List<Class<?>> expectedTypes) throws IOException { |
| int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case 'T': |
| return Boolean.valueOf(true); |
| |
| case 'F': |
| return Boolean.valueOf(false); |
| |
| // direct integer |
| case 0x80: |
| case 0x81: |
| case 0x82: |
| case 0x83: |
| case 0x84: |
| case 0x85: |
| case 0x86: |
| case 0x87: |
| case 0x88: |
| case 0x89: |
| case 0x8a: |
| case 0x8b: |
| case 0x8c: |
| case 0x8d: |
| case 0x8e: |
| case 0x8f: |
| |
| case 0x90: |
| case 0x91: |
| case 0x92: |
| case 0x93: |
| case 0x94: |
| case 0x95: |
| case 0x96: |
| case 0x97: |
| case 0x98: |
| case 0x99: |
| case 0x9a: |
| case 0x9b: |
| case 0x9c: |
| case 0x9d: |
| case 0x9e: |
| case 0x9f: |
| |
| case 0xa0: |
| case 0xa1: |
| case 0xa2: |
| case 0xa3: |
| case 0xa4: |
| case 0xa5: |
| case 0xa6: |
| case 0xa7: |
| case 0xa8: |
| case 0xa9: |
| case 0xaa: |
| case 0xab: |
| case 0xac: |
| case 0xad: |
| case 0xae: |
| case 0xaf: |
| |
| case 0xb0: |
| case 0xb1: |
| case 0xb2: |
| case 0xb3: |
| case 0xb4: |
| case 0xb5: |
| case 0xb6: |
| case 0xb7: |
| case 0xb8: |
| case 0xb9: |
| case 0xba: |
| case 0xbb: |
| case 0xbc: |
| case 0xbd: |
| case 0xbe: |
| case 0xbf: |
| return Integer.valueOf(tag - BC_INT_ZERO); |
| |
| /* byte int */ |
| case 0xc0: |
| case 0xc1: |
| case 0xc2: |
| case 0xc3: |
| case 0xc4: |
| case 0xc5: |
| case 0xc6: |
| case 0xc7: |
| case 0xc8: |
| case 0xc9: |
| case 0xca: |
| case 0xcb: |
| case 0xcc: |
| case 0xcd: |
| case 0xce: |
| case 0xcf: |
| return Integer.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read()); |
| |
| /* short int */ |
| case 0xd0: |
| case 0xd1: |
| case 0xd2: |
| case 0xd3: |
| case 0xd4: |
| case 0xd5: |
| case 0xd6: |
| case 0xd7: |
| return Integer.valueOf(((tag - BC_INT_SHORT_ZERO) << 16) |
| + 256 * read() + read()); |
| |
| case 'I': |
| return Integer.valueOf(parseInt()); |
| |
| // direct long |
| case 0xd8: |
| case 0xd9: |
| case 0xda: |
| case 0xdb: |
| case 0xdc: |
| case 0xdd: |
| case 0xde: |
| case 0xdf: |
| |
| case 0xe0: |
| case 0xe1: |
| case 0xe2: |
| case 0xe3: |
| case 0xe4: |
| case 0xe5: |
| case 0xe6: |
| case 0xe7: |
| case 0xe8: |
| case 0xe9: |
| case 0xea: |
| case 0xeb: |
| case 0xec: |
| case 0xed: |
| case 0xee: |
| case 0xef: |
| return Long.valueOf(tag - BC_LONG_ZERO); |
| |
| /* byte long */ |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| case 0xf8: |
| case 0xf9: |
| case 0xfa: |
| case 0xfb: |
| case 0xfc: |
| case 0xfd: |
| case 0xfe: |
| case 0xff: |
| return Long.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read()); |
| |
| /* short long */ |
| case 0x38: |
| case 0x39: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| return Long.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read()); |
| |
| case BC_LONG_INT: |
| return Long.valueOf(parseInt()); |
| |
| case 'L': |
| return Long.valueOf(parseLong()); |
| |
| case BC_DOUBLE_ZERO: |
| return Double.valueOf(0); |
| |
| case BC_DOUBLE_ONE: |
| return Double.valueOf(1); |
| |
| case BC_DOUBLE_BYTE: |
| return Double.valueOf((byte) read()); |
| |
| case BC_DOUBLE_SHORT: |
| return Double.valueOf((short) (256 * read() + read())); |
| |
| case BC_DOUBLE_MILL: { |
| int mills = parseInt(); |
| |
| return Double.valueOf(0.001 * mills); |
| } |
| |
| case 'D': |
| return Double.valueOf(parseDouble()); |
| |
| case BC_DATE: |
| return new Date(parseLong()); |
| |
| case BC_DATE_MINUTE: |
| return new Date(parseInt() * 60000L); |
| |
| case BC_STRING_CHUNK: |
| case 'S': { |
| _isLastChunk = tag == 'S'; |
| _chunkLength = (read() << 8) + read(); |
| |
| _sbuf.setLength(0); |
| |
| parseString(_sbuf); |
| |
| return _sbuf.toString(); |
| } |
| |
| case 0x00: |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| case 0x04: |
| case 0x05: |
| case 0x06: |
| case 0x07: |
| case 0x08: |
| case 0x09: |
| case 0x0a: |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| |
| case 0x10: |
| case 0x11: |
| case 0x12: |
| case 0x13: |
| case 0x14: |
| case 0x15: |
| case 0x16: |
| case 0x17: |
| case 0x18: |
| case 0x19: |
| case 0x1a: |
| case 0x1b: |
| case 0x1c: |
| case 0x1d: |
| case 0x1e: |
| case 0x1f: { |
| _isLastChunk = true; |
| _chunkLength = tag - 0x00; |
| |
| _sbuf.setLength(0); |
| |
| parseString(_sbuf); |
| |
| return _sbuf.toString(); |
| } |
| |
| case 0x30: |
| case 0x31: |
| case 0x32: |
| case 0x33: { |
| _isLastChunk = true; |
| _chunkLength = (tag - 0x30) * 256 + read(); |
| |
| _sbuf.setLength(0); |
| |
| parseString(_sbuf); |
| |
| return _sbuf.toString(); |
| } |
| |
| case BC_BINARY_CHUNK: |
| case 'B': { |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| |
| int data; |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| while ((data = parseByte()) >= 0) |
| bos.write(data); |
| |
| return bos.toByteArray(); |
| } |
| |
| case 0x20: |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| case 0x25: |
| case 0x26: |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2d: |
| case 0x2e: |
| case 0x2f: { |
| _isLastChunk = true; |
| int len = tag - 0x20; |
| _chunkLength = 0; |
| |
| byte[] data = new byte[len]; |
| |
| for (int i = 0; i < len; i++) |
| data[i] = (byte) read(); |
| |
| return data; |
| } |
| |
| case 0x34: |
| case 0x35: |
| case 0x36: |
| case 0x37: { |
| _isLastChunk = true; |
| int len = (tag - 0x34) * 256 + read(); |
| _chunkLength = 0; |
| |
| byte[] buffer = new byte[len]; |
| |
| for (int i = 0; i < len; i++) { |
| buffer[i] = (byte) read(); |
| } |
| |
| return buffer; |
| } |
| |
| case BC_LIST_VARIABLE: { |
| // variable length list |
| String type = readType(); |
| |
| return findSerializerFactory().readList(this, -1, type); |
| } |
| |
| case BC_LIST_VARIABLE_UNTYPED: { |
| return findSerializerFactory().readList(this, -1, null); |
| } |
| |
| case BC_LIST_FIXED: { |
| // fixed length lists |
| String type = readType(); |
| int length = readInt(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(type, null); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.size() == 1; |
| |
| return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null); |
| } |
| |
| case BC_LIST_FIXED_UNTYPED: { |
| // fixed length lists |
| int length = readInt(); |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, null); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.size() == 1; |
| |
| return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null); |
| } |
| |
| // compact fixed list |
| case 0x70: |
| case 0x71: |
| case 0x72: |
| case 0x73: |
| case 0x74: |
| case 0x75: |
| case 0x76: |
| case 0x77: { |
| // fixed length lists |
| String type = readType(); |
| int length = tag - 0x70; |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(type, null); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.size() == 1; |
| |
| return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null); |
| } |
| |
| // compact fixed untyped list |
| case 0x78: |
| case 0x79: |
| case 0x7a: |
| case 0x7b: |
| case 0x7c: |
| case 0x7d: |
| case 0x7e: |
| case 0x7f: { |
| // fixed length lists |
| int length = tag - 0x78; |
| |
| Deserializer reader; |
| reader = findSerializerFactory().getListDeserializer(null, null); |
| |
| boolean valueType = expectedTypes != null && expectedTypes.size() == 1; |
| |
| return reader.readLengthList(this, length, valueType ? expectedTypes.get(0) : null); |
| } |
| |
| case 'H': { |
| |
| boolean keyValuePair = expectedTypes != null && expectedTypes.size() == 2; |
| |
| // fix deserialize of short type |
| Deserializer reader; |
| reader = findSerializerFactory().getDeserializer(Map.class); |
| |
| return reader.readMap(this |
| , keyValuePair ? expectedTypes.get(0) : null |
| , keyValuePair ? expectedTypes.get(1) : null); |
| } |
| |
| case 'M': { |
| String type = readType(); |
| |
| return findSerializerFactory().readMap(this, type); |
| } |
| |
| case 'C': { |
| readObjectDefinition(null); |
| |
| return readObject(); |
| } |
| |
| case 0x60: |
| case 0x61: |
| case 0x62: |
| case 0x63: |
| case 0x64: |
| case 0x65: |
| case 0x66: |
| case 0x67: |
| case 0x68: |
| case 0x69: |
| case 0x6a: |
| case 0x6b: |
| case 0x6c: |
| case 0x6d: |
| case 0x6e: |
| case 0x6f: { |
| int ref = tag - 0x60; |
| |
| if (_classDefs == null) |
| throw error("No classes defined at reference '{0}'" + tag); |
| |
| ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref); |
| |
| return readObjectInstance(null, def); |
| } |
| |
| case 'O': { |
| int ref = readInt(); |
| |
| ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref); |
| |
| return readObjectInstance(null, def); |
| } |
| |
| case BC_REF: { |
| int ref = readInt(); |
| |
| return _refs.get(ref); |
| } |
| |
| default: |
| if (tag < 0) |
| throw new EOFException("readObject: unexpected end of file"); |
| else |
| throw error("readObject: unknown code " + codeName(tag)); |
| } |
| } |
| |
| private void parseString(StringBuilder sbuf) |
| throws IOException { |
| while (true) { |
| if (_chunkLength <= 0) { |
| if (!parseChunkLength()) |
| return; |
| } |
| |
| int length = _chunkLength; |
| _chunkLength = 0; |
| |
| while (length-- > 0) { |
| sbuf.append((char) parseUTF8Char()); |
| } |
| } |
| } |
| |
| /** |
| * Reads an object definition: |
| * <p> |
| * <pre> |
| * O string <int> (string)* <value>* |
| * </pre> |
| */ |
| private void readObjectDefinition(Class cl) |
| throws IOException { |
| String type = readString(); |
| int len = readInt(); |
| |
| String[] fieldNames = new String[len]; |
| for (int i = 0; i < len; i++) |
| fieldNames[i] = readString(); |
| |
| ObjectDefinition def = new ObjectDefinition(type, fieldNames); |
| |
| if (_classDefs == null) |
| _classDefs = new ArrayList(); |
| |
| _classDefs.add(def); |
| } |
| |
| private Object readObjectInstance(Class cl, ObjectDefinition def) |
| throws IOException { |
| String type = def.getType(); |
| String[] fieldNames = def.getFieldNames(); |
| |
| if (cl != null) { |
| Deserializer reader; |
| reader = findSerializerFactory().getObjectDeserializer(type, cl); |
| |
| return reader.readObject(this, fieldNames); |
| } else { |
| return findSerializerFactory().readObject(this, type, fieldNames); |
| } |
| } |
| |
| private String readLenString() |
| throws IOException { |
| int len = readInt(); |
| |
| _isLastChunk = true; |
| _chunkLength = len; |
| |
| _sbuf.setLength(0); |
| int ch; |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| } |
| |
| private String readLenString(int len) |
| throws IOException { |
| _isLastChunk = true; |
| _chunkLength = len; |
| |
| _sbuf.setLength(0); |
| int ch; |
| while ((ch = parseChar()) >= 0) |
| _sbuf.append((char) ch); |
| |
| return _sbuf.toString(); |
| } |
| |
| /** |
| * Reads a remote object. |
| */ |
| @Override |
| public Object readRemote() |
| throws IOException { |
| String type = readType(); |
| String url = readString(); |
| |
| return resolveRemote(type, url); |
| } |
| |
| /** |
| * Reads a reference. |
| */ |
| @Override |
| public Object readRef() |
| throws IOException { |
| return _refs.get(parseInt()); |
| } |
| |
| /** |
| * Reads the start of a list. |
| */ |
| @Override |
| public int readListStart() |
| throws IOException { |
| return read(); |
| } |
| |
| /** |
| * Reads the start of a list. |
| */ |
| @Override |
| public int readMapStart() |
| throws IOException { |
| return read(); |
| } |
| |
| /** |
| * Returns true if this is the end of a list or a map. |
| */ |
| @Override |
| public boolean isEnd() |
| throws IOException { |
| int code; |
| |
| if (_offset < _length) |
| code = (_buffer[_offset] & 0xff); |
| else { |
| code = read(); |
| |
| if (code >= 0) |
| _offset--; |
| } |
| |
| return (code < 0 || code == 'Z'); |
| } |
| |
| /** |
| * Reads the end byte. |
| */ |
| @Override |
| public void readEnd() |
| throws IOException { |
| int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| if (code == 'Z') |
| return; |
| else if (code < 0) |
| throw error("unexpected end of file"); |
| else |
| throw error("unknown code:" + codeName(code)); |
| } |
| |
| /** |
| * Reads the end byte. |
| */ |
| @Override |
| public void readMapEnd() |
| throws IOException { |
| int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| if (code != 'Z') |
| throw error("expected end of map ('Z') at '" + codeName(code) + "'"); |
| } |
| |
| /** |
| * Reads the end byte. |
| */ |
| @Override |
| public void readListEnd() |
| throws IOException { |
| int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| if (code != 'Z') |
| throw error("expected end of list ('Z') at '" + codeName(code) + "'"); |
| } |
| |
| /** |
| * Adds a list/map reference. |
| */ |
| @Override |
| public int addRef(Object ref) { |
| if (_refs == null) |
| _refs = new ArrayList(); |
| |
| _refs.add(ref); |
| |
| return _refs.size() - 1; |
| } |
| |
| /** |
| * Adds a list/map reference. |
| */ |
| @Override |
| public void setRef(int i, Object ref) { |
| _refs.set(i, ref); |
| } |
| |
| /** |
| * Resets the references for streaming. |
| */ |
| @Override |
| public void resetReferences() { |
| if (_refs != null) |
| _refs.clear(); |
| } |
| |
| public Object readStreamingObject() |
| throws IOException { |
| if (_refs != null) |
| _refs.clear(); |
| |
| return readObject(); |
| } |
| |
| /** |
| * 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 HessianRemote(type, url); |
| } |
| |
| /** |
| * Parses a type from the stream. |
| * <p> |
| * <pre> |
| * type ::= string |
| * type ::= int |
| * </pre> |
| */ |
| @Override |
| public String readType() |
| throws IOException { |
| int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| _offset--; |
| |
| switch (code) { |
| case 0x00: |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| case 0x04: |
| case 0x05: |
| case 0x06: |
| case 0x07: |
| case 0x08: |
| case 0x09: |
| case 0x0a: |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| |
| case 0x10: |
| case 0x11: |
| case 0x12: |
| case 0x13: |
| case 0x14: |
| case 0x15: |
| case 0x16: |
| case 0x17: |
| case 0x18: |
| case 0x19: |
| case 0x1a: |
| case 0x1b: |
| case 0x1c: |
| case 0x1d: |
| case 0x1e: |
| case 0x1f: |
| |
| case 0x30: |
| case 0x31: |
| case 0x32: |
| case 0x33: |
| case BC_STRING_CHUNK: |
| case 'S': { |
| String type = readString(); |
| |
| if (_types == null) |
| _types = new ArrayList(); |
| |
| _types.add(type); |
| |
| return type; |
| } |
| |
| default: { |
| int ref = readInt(); |
| |
| if (_types.size() <= ref) |
| throw new IndexOutOfBoundsException("type ref #" + ref + " is greater than the number of valid types (" + _types.size() + ")"); |
| |
| return (String) _types.get(ref); |
| } |
| } |
| } |
| |
| /** |
| * Parses the length for an array |
| * <p> |
| * <pre> |
| * l b32 b24 b16 b8 |
| * </pre> |
| */ |
| @Override |
| public int readLength() |
| throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Parses a 32-bit integer value from the stream. |
| * <p> |
| * <pre> |
| * b32 b24 b16 b8 |
| * </pre> |
| */ |
| private int parseInt() |
| throws IOException { |
| int offset = _offset; |
| |
| if (offset + 3 < _length) { |
| byte[] buffer = _buffer; |
| |
| int b32 = buffer[offset + 0] & 0xff; |
| int b24 = buffer[offset + 1] & 0xff; |
| int b16 = buffer[offset + 2] & 0xff; |
| int b8 = buffer[offset + 3] & 0xff; |
| |
| _offset = offset + 4; |
| |
| return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8; |
| } else { |
| int b32 = read(); |
| int b24 = read(); |
| int b16 = read(); |
| int b8 = read(); |
| |
| return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8; |
| } |
| } |
| |
| /** |
| * Parses a 64-bit long value from the stream. |
| * <p> |
| * <pre> |
| * b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| private long parseLong() |
| throws IOException { |
| long b64 = read(); |
| long b56 = read(); |
| long b48 = read(); |
| long b40 = read(); |
| long b32 = read(); |
| long b24 = read(); |
| long b16 = read(); |
| long b8 = read(); |
| |
| return ((b64 << 56) |
| + (b56 << 48) |
| + (b48 << 40) |
| + (b40 << 32) |
| + (b32 << 24) |
| + (b24 << 16) |
| + (b16 << 8) |
| + b8); |
| } |
| |
| /** |
| * Parses a 64-bit double value from the stream. |
| * <p> |
| * <pre> |
| * b64 b56 b48 b40 b32 b24 b16 b8 |
| * </pre> |
| */ |
| private double parseDouble() |
| throws IOException { |
| long bits = parseLong(); |
| |
| return Double.longBitsToDouble(bits); |
| } |
| |
| org.w3c.dom.Node parseXML() |
| throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private boolean parseChunkLength() |
| throws IOException { |
| if (_isLastChunk) |
| return false; |
| |
| int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| switch (code) { |
| case BC_STRING_CHUNK: |
| _isLastChunk = false; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 'S': |
| _isLastChunk = true; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 0x00: |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| case 0x04: |
| case 0x05: |
| case 0x06: |
| case 0x07: |
| case 0x08: |
| case 0x09: |
| case 0x0a: |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| |
| case 0x10: |
| case 0x11: |
| case 0x12: |
| case 0x13: |
| case 0x14: |
| case 0x15: |
| case 0x16: |
| case 0x17: |
| case 0x18: |
| case 0x19: |
| case 0x1a: |
| case 0x1b: |
| case 0x1c: |
| case 0x1d: |
| case 0x1e: |
| case 0x1f: |
| _isLastChunk = true; |
| _chunkLength = code - 0x00; |
| break; |
| |
| case 0x30: |
| case 0x31: |
| case 0x32: |
| case 0x33: |
| _isLastChunk = true; |
| _chunkLength = (code - 0x30) * 256 + read(); |
| break; |
| |
| default: |
| throw expect("string", code); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Reads a character from the underlying stream. |
| */ |
| private int parseChar() |
| throws IOException { |
| while (_chunkLength <= 0) { |
| if (!parseChunkLength()) |
| return -1; |
| } |
| |
| _chunkLength--; |
| |
| return parseUTF8Char(); |
| } |
| |
| /** |
| * Parses a single UTF8 character. |
| */ |
| private int parseUTF8Char() |
| throws IOException { |
| int ch = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); |
| |
| if (ch < 0x80) |
| return ch; |
| else if ((ch & 0xe0) == 0xc0) { |
| int ch1 = read(); |
| int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f); |
| |
| return v; |
| } else if ((ch & 0xf0) == 0xe0) { |
| int ch1 = read(); |
| int ch2 = read(); |
| int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f); |
| |
| return v; |
| } else |
| throw error("bad utf-8 encoding at " + codeName(ch)); |
| } |
| |
| /** |
| * Reads a byte from the underlying stream. |
| */ |
| private int parseByte() |
| throws IOException { |
| while (_chunkLength <= 0) { |
| if (_isLastChunk) { |
| return -1; |
| } |
| |
| int code = read(); |
| |
| switch (code) { |
| case BC_BINARY_CHUNK: |
| _isLastChunk = false; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 'B': |
| _isLastChunk = true; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 0x20: |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| case 0x25: |
| case 0x26: |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2d: |
| case 0x2e: |
| case 0x2f: |
| _isLastChunk = true; |
| |
| _chunkLength = code - 0x20; |
| break; |
| |
| case 0x34: |
| case 0x35: |
| case 0x36: |
| case 0x37: |
| _isLastChunk = true; |
| _chunkLength = (code - 0x34) * 256 + read(); |
| break; |
| |
| default: |
| throw expect("byte[]", code); |
| } |
| } |
| |
| _chunkLength--; |
| |
| return read(); |
| } |
| |
| /** |
| * Reads bytes based on an input stream. |
| */ |
| @Override |
| public InputStream readInputStream() |
| throws IOException { |
| int tag = read(); |
| |
| switch (tag) { |
| case 'N': |
| return null; |
| |
| case BC_BINARY: |
| case BC_BINARY_CHUNK: |
| case 'b': //maybe it's a mistype of BC_BINARY_CHUNK |
| _isLastChunk = tag == 'B'; |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 0x20: |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| case 0x25: |
| case 0x26: |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2d: |
| case 0x2e: |
| case 0x2f: |
| _isLastChunk = true; |
| _chunkLength = tag - 0x20; |
| break; |
| case 0x34: case 0x35: case 0x36: case 0x37: |
| _isLastChunk = true; |
| _chunkLength = (tag - 0x34) * 256 + read(); |
| break; |
| default: |
| throw expect("binary", tag); |
| } |
| |
| return new ReadInputStream(); |
| } |
| |
| /** |
| * Reads bytes from the underlying stream. |
| */ |
| int read(byte[] buffer, int offset, int length) |
| throws IOException { |
| int readLength = 0; |
| |
| while (length > 0) { |
| while (_chunkLength <= 0) { |
| if (_isLastChunk) |
| return readLength == 0 ? -1 : readLength; |
| |
| int code = read(); |
| |
| switch (code) { |
| case BC_BINARY_CHUNK: |
| case 'b': //maybe it's a mistype of BC_BINARY_CHUNK |
| _isLastChunk = false; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case BC_BINARY: |
| _isLastChunk = true; |
| |
| _chunkLength = (read() << 8) + read(); |
| break; |
| |
| case 0x20: |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| case 0x25: |
| case 0x26: |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2d: |
| case 0x2e: |
| case 0x2f: |
| _isLastChunk = true; |
| _chunkLength = code - 0x20; |
| break; |
| case 0x34: case 0x35: case 0x36: case 0x37: |
| _isLastChunk = true; |
| _chunkLength = (code - 0x34) * 256 + read(); |
| break; |
| default: |
| throw expect("byte[]", code); |
| } |
| } |
| |
| int sublen = _chunkLength; |
| if (length < sublen) |
| sublen = length; |
| |
| if (_length <= _offset && !readBuffer()) |
| return -1; |
| |
| if (_length - _offset < sublen) |
| sublen = _length - _offset; |
| |
| System.arraycopy(_buffer, _offset, buffer, offset, sublen); |
| |
| _offset += sublen; |
| |
| offset += sublen; |
| readLength += sublen; |
| length -= sublen; |
| _chunkLength -= sublen; |
| } |
| |
| return readLength; |
| } |
| |
| /** |
| * Normally, shouldn't be called externally, but needed for QA, e.g. |
| * ejb/3b01. |
| */ |
| public final int read() |
| throws IOException { |
| if (_length <= _offset && !readBuffer()) |
| return -1; |
| |
| return _buffer[_offset++] & 0xff; |
| } |
| |
| private final boolean readBuffer() |
| throws IOException { |
| byte[] buffer = _buffer; |
| int offset = _offset; |
| int length = _length; |
| |
| if (offset < length) { |
| System.arraycopy(buffer, offset, buffer, 0, length - offset); |
| offset = length - offset; |
| } else |
| offset = 0; |
| |
| int len = _is.read(buffer, offset, SIZE - offset); |
| |
| if (len <= 0) { |
| _length = offset; |
| _offset = 0; |
| |
| return offset > 0; |
| } |
| |
| _length = offset + len; |
| _offset = 0; |
| |
| return true; |
| } |
| |
| @Override |
| public Reader getReader() { |
| return null; |
| } |
| |
| protected IOException expect(String expect, int ch) |
| throws IOException { |
| if (ch < 0) |
| return error("expected " + expect + " at end of file"); |
| else { |
| _offset--; |
| |
| try { |
| Object obj = readObject(); |
| |
| if (obj != null) { |
| return error("expected " + expect |
| + " at 0x" + Integer.toHexString(ch & 0xff) |
| + " " + obj.getClass().getName() + " (" + obj + ")"); |
| } else |
| return error("expected " + expect |
| + " at 0x" + Integer.toHexString(ch & 0xff) + " null"); |
| } catch (IOException e) { |
| log.log(Level.FINE, e.toString(), e); |
| |
| return error("expected " + expect |
| + " at 0x" + Integer.toHexString(ch & 0xff)); |
| } |
| } |
| } |
| |
| protected String codeName(int ch) { |
| if (ch < 0) |
| return "end of file"; |
| else |
| return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) +ch + ")"; |
| } |
| |
| protected IOException error(String message) { |
| if (_method != null) |
| return new HessianProtocolException(_method + ": " + message); |
| else |
| return new HessianProtocolException(message); |
| } |
| |
| @Override |
| public void close() |
| throws IOException { |
| InputStream is = _is; |
| _is = null; |
| |
| if (_isCloseStreamOnClose && is != null) |
| is.close(); |
| } |
| |
| ; |
| |
| final static class ObjectDefinition { |
| private final String _type; |
| private final String[] _fields; |
| |
| ObjectDefinition(String type, String[] fields) { |
| _type = type; |
| _fields = fields; |
| } |
| |
| String getType() { |
| return _type; |
| } |
| |
| String[] getFieldNames() { |
| return _fields; |
| } |
| } |
| |
| class ReadInputStream extends InputStream { |
| boolean _isClosed = false; |
| |
| @Override |
| public int read() |
| throws IOException { |
| if (_isClosed) |
| return -1; |
| |
| int ch = parseByte(); |
| if (ch < 0) |
| _isClosed = true; |
| |
| return ch; |
| } |
| |
| @Override |
| public int read(byte[] buffer, int offset, int length) |
| throws IOException { |
| if (_isClosed) |
| return -1; |
| |
| int len = Hessian2Input.this.read(buffer, offset, length); |
| if (len < 0) |
| _isClosed = true; |
| |
| return len; |
| } |
| |
| @Override |
| public void close() |
| throws IOException { |
| while (read() >= 0) { |
| } |
| } |
| } |
| } |