content: /*
   * 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.hessian.micro;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Date;
  
  /**
   * Input stream for Hessian requests, compatible with microedition
   * Java.  It only uses classes and types available to J2ME.  In
   * particular, it does not have any support for the &lt;double> type.
   *
   * <p>MicroHessianInput does not depend on any classes other than
   * in J2ME, so it can be extracted independently into a smaller package.
   *
   * <p>MicroHessianInput is unbuffered, so any client needs to provide
   * its own buffering.
   *
   * <pre>
   * InputStream is = ...; // from http connection
   * MicroHessianInput in = new MicroHessianInput(is);
   * String value;
   *
   * in.startReply();         // read reply header
   * value = in.readString(); // read string value
   * in.completeReply();      // read reply footer
   * </pre>
   */
  public class MicroHessianInput {
    protected InputStream is;
    /**
     * Creates a new Hessian input stream, initialized with an
     * underlying input stream.
     *
     * @param is the underlying input stream.
     */
    public MicroHessianInput(InputStream is)
    {
      init(is);
    }
  
    /**
     * Creates an uninitialized Hessian input stream.
     */
    public MicroHessianInput()
    {
    }
  
    /**
     * Initialize the hessian stream with the underlying input stream.
     */
    public void init(InputStream is)
    {
      this.is = is;
    }
  
    /**
     * Starts reading the reply
     *
     * <p>A successful completion will have a single value:
     *
     * <pre>
     * r x01 x00
     * </pre>
     */
    public void startReply()
      throws IOException
    {
      int tag = is.read();
      
      if (tag != 'r')
        protocolException("expected hessian reply");
  
      int major = is.read();
      int minor = is.read();
    }
  
    /**
     * Completes reading the call
     *
     * <p>A successful completion will have a single value:
     *
     * <pre>
     * z
     * </pre>
     */
    public void completeReply()
      throws IOException
    {
      int tag = is.read();
      
      if (tag != 'z')
        protocolException("expected end of reply");
    }
  
    /**
     * Reads a boolean
     *
     * <pre>
     * T
     * F
     * </pre>
     */
    public boolean readBoolean()
      throws IOException
    {
      int tag = is.read();
  
      switch (tag) {
      case 'T': return true;
      case 'F': return false;
      default:
        throw expect("boolean", tag);
      }
    }
  
    /**
     * Reads an integer
     *
     * <pre>
     * I b32 b24 b16 b8
     * </pre>
     */
    public int readInt()
      throws IOException
    {
      int tag = is.read();
  
      if (tag != 'I')
        throw expect("integer", tag);
  
      int b32 = is.read();
      int b24 = is.read();
      int b16 = is.read();
      int b8 = is.read();
  
      return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8;
    }
  
    /**
     * Reads a long
     *
     * <pre>
     * L b64 b56 b48 b40 b32 b24 b16 b8
     * </pre>
     */
    public long readLong()
      throws IOException
    {
      int tag = is.read();
  
      if (tag != 'L')
        throw protocolException("expected long");
  
      long b64 = is.read();
      long b56 = is.read();
      long b48 = is.read();
      long b40 = is.read();
      long b32 = is.read();
      long b24 = is.read();
      long b16 = is.read();
      long b8 = is.read();
  
      return ((b64 << 56) +
              (b56 << 48) +
              (b48 << 40) +
              (b40 << 32) +
              (b32 << 24) +
              (b24 << 16) +
              (b16 << 8) +
              b8);
    }
  
    /**
     * Reads a date.
     *
     * <pre>
     * T b64 b56 b48 b40 b32 b24 b16 b8
     * </pre>
     */
    public long readUTCDate()
      throws IOException
    {
      int tag = is.read();
  
      if (tag != 'd')
        throw protocolException("expected date");
  
      long b64 = is.read();
      long b56 = is.read();
      long b48 = is.read();
      long b40 = is.read();
      long b32 = is.read();
      long b24 = is.read();
      long b16 = is.read();
      long b8 = is.read();
  
      return ((b64 << 56) +
              (b56 << 48) +
              (b48 << 40) +
              (b40 << 32) +
              (b32 << 24) +
              (b24 << 16) +
              (b16 << 8) +
              b8);
    }
  
    /**
     * Reads a string
     *
     * <pre>
     * S b16 b8 string value
     * </pre>
     */
    public String readString()
      throws IOException
    {
      int tag = is.read();
  
      if (tag == 'N')
        return null;
  
      if (tag != 'S')
        throw expect("string", tag);
  
      int b16 = is.read();
      int b8 = is.read();
  
      int len = (b16 << 8) + b8;
  
      return readStringImpl(len);
    }
  
    /**
     * Reads a byte array
     *
     * <pre>
     * B b16 b8 data value
     * </pre>
     */
    public byte []readBytes()
      throws IOException
    {
      int tag = is.read();
  
      if (tag == 'N')
        return null;
  
      if (tag != 'B')
        throw expect("bytes", tag);
  
      int b16 = is.read();
      int b8 = is.read();
  
      int len = (b16 << 8) + b8;
  
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
  
      for (int i = 0; i < len; i++)
        bos.write(is.read());
  
      return bos.toByteArray();
    }
  
    /**
     * Reads an arbitrary object the input stream.
     */
    public Object readObject(Class expectedClass)
      throws IOException
    {
      int tag = is.read();
  
      switch (tag) {
      case 'N':
        return null;
        
      case 'T':
        return new Boolean(true);
        
      case 'F':
        return new Boolean(false);
        
      case 'I': {
        int b32 = is.read();
        int b24 = is.read();
        int b16 = is.read();
        int b8 = is.read();
  
        return new Integer((b32 << 24) + (b24 << 16) + (b16 << 8) + b8);
      }
      
      case 'L': {
        long b64 = is.read();
        long b56 = is.read();
        long b48 = is.read();
        long b40 = is.read();
        long b32 = is.read();
        long b24 = is.read();
        long b16 = is.read();
        long b8 = is.read();
  
        return new Long((b64 << 56) +
                        (b56 << 48) +
                        (b48 << 40) +
                        (b40 << 32) +
                        (b32 << 24) +
                        (b24 << 16) +
                        (b16 << 8) +
                        b8);
      }
      
      case 'd': {
        long b64 = is.read();
        long b56 = is.read();
        long b48 = is.read();
        long b40 = is.read();
        long b32 = is.read();
        long b24 = is.read();
        long b16 = is.read();
        long b8 = is.read();
  
        return new Date((b64 << 56) +
                        (b56 << 48) +
                        (b48 << 40) +
                        (b40 << 32) +
                        (b32 << 24) +
                        (b24 << 16) +
                        (b16 << 8) +
                        b8);
      }
      
      case 'S':
      case 'X': {
        int b16 = is.read();
        int b8 = is.read();
  
        int len = (b16 << 8) + b8;
  
        return readStringImpl(len);
      }
      
      case 'B': {
        if (tag != 'B')
          throw expect("bytes", tag);
  
        int b16 = is.read();
        int b8 = is.read();
  
        int len = (b16 << 8) + b8;
  
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
  
        for (int i = 0; i < len; i++)
          bos.write(is.read());
  
        return bos.toByteArray();
      }
      default:
        throw new IOException("unknown code:" + (char) tag);
      }
    }
  
    /**
     * Reads a string from the underlying stream.
     */
    protected String readStringImpl(int length)
      throws IOException
    {
      StringBuffer sb = new StringBuffer();
      
      for (int i = 0; i < length; i++) {
        int ch = is.read();
  
        if (ch < 0x80)
          sb.append((char) ch);
        else if ((ch & 0xe0) == 0xc0) {
          int ch1 = is.read();
          int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f);
  
          sb.append((char) v);
        }
        else if ((ch & 0xf0) == 0xe0) {
          int ch1 = is.read();
          int ch2 = is.read();
          int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f);
  
          sb.append((char) v);
        }
        else
          throw new IOException("bad utf-8 encoding");
      }
  
      return sb.toString();
    }
  
    protected IOException expect(String expect, int ch)
    {
      if (ch < 0)
        return protocolException("expected " + expect + " at end of file");
      else
        return protocolException("expected " + expect + " at " + (char) ch);
    }
    
    protected IOException protocolException(String message)
    {
      return new IOException(message);
    }
  }
  