//
// JmsaStreamMessage.java - JMS Adapter StreamMessage
//
// StreamMessages hold a sequence of java Objects.
// The Objects are of a limited set of primitive Java types. 
// We simply keep copies of them them in a Vector.
//

package nyangau.jmsadapter;

import java.util.*;
import javax.jms.*;

public class JmsaStreamMessage extends JmsaMessage implements StreamMessage
  {
  protected Vector<Object> objs;

  // when bro
  protected int ptr;
  protected int bo; // byte offset within byte[]

//...scantConvertObject:2:
protected static String cantConvertObject(Object o, String to)
  {
  return cantConvert("object in StreamMessage", o, to);
  }
//...e

//...sBoolean:2:
// from Boolean, String
 
public boolean readBoolean()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Boolean.valueOf(null).booleanValue();
  if ( o instanceof Boolean )
    return ((Boolean)o).booleanValue();
  if ( o instanceof String )
    return Boolean.valueOf((String)o).booleanValue();
  throw new MessageFormatException(cantConvertObject(o, "boolean"));
  }

public void writeBoolean(boolean value)
  throws JMSException
  { writeObjectInternal(new Boolean(value)); }
//...e
//...sCharacter:2:
// from Character

public char readChar()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    throw new NullPointerException();
  if ( o instanceof Character )
    return ((Character)o).charValue();
  throw new MessageFormatException(cantConvertObject(o, "char"));
  }

public void writeChar(char value)
  throws JMSException
  { writeObjectInternal(new Character(value)); }
//...e
//...sByte:2:
// from Byte, String

public byte readByte()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Byte.parseByte(null);
  if ( o instanceof Byte )
    return ((Byte)o).byteValue();
  if ( o instanceof String )
    return Byte.parseByte((String)o);
  throw new MessageFormatException(cantConvertObject(o, "byte"));
  }

public void writeByte(byte value)
  throws JMSException
  { writeObjectInternal(new Byte(value)); }
//...e
//...sShort:2:
// from Short, String, Byte

public short readShort()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Short.parseShort(null);
  if ( o instanceof Short )
    return ((Short)o).shortValue();
  if ( o instanceof String )
    return Short.parseShort((String)o);
  if ( o instanceof Byte )
    return ((Byte)o).shortValue();
  throw new MessageFormatException(cantConvertObject(o, "short"));
  }

public void writeShort(short value)
  throws JMSException
  { writeObjectInternal(new Short(value)); }
//...e
//...sInt:2:
// from Integer, String, Short, Byte

public int readInt()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Integer.parseInt(null);
  if ( o instanceof Integer )
    return ((Integer)o).intValue();
  if ( o instanceof String )
    return Integer.parseInt((String)o);
  if ( o instanceof Short )
    return ((Short)o).intValue();
  if ( o instanceof Byte )
    return ((Byte)o).intValue();
  throw new MessageFormatException(cantConvertObject(o, "int"));
  }

public void writeInt(int value)
  throws JMSException
  { writeObjectInternal(new Integer(value)); }
//...e
//...sLong:2:
// from Long, String, Integer, Short, Byte

public long readLong()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Long.parseLong(null);
  if ( o instanceof Long )
    return ((Long)o).longValue();
  if ( o instanceof String )
    return Long.parseLong((String)o);
  if ( o instanceof Integer )
    return ((Integer)o).longValue();
  if ( o instanceof Short )
    return ((Short)o).longValue();
  if ( o instanceof Byte )
    return ((Byte)o).longValue();
  throw new MessageFormatException(cantConvertObject(o, "long"));
  }

public void writeLong(long value)
  throws JMSException
  { writeObjectInternal(new Long(value)); }
//...e
//...sFloat:2:
// from Float, String

public float readFloat()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Float.parseFloat(null);
  if ( o instanceof Float )
    return ((Float)o).floatValue();
  if ( o instanceof String )
    return Float.parseFloat((String)o);
  throw new MessageFormatException(cantConvertObject(o, "float"));
  }

public void writeFloat(float value)
  throws JMSException
  { writeObjectInternal(new Float(value)); }
//...e
//...sDouble:2:
// from Double, String, Float

public double readDouble()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return Double.parseDouble(null);
  if ( o instanceof Double )
    return ((Double)o).doubleValue();
  if ( o instanceof String )
    return Double.parseDouble((String)o);
  if ( o instanceof Float )
    return ((Float)o).doubleValue();
  throw new MessageFormatException(cantConvertObject(o, "double"));
  }

public void writeDouble(double value)
  throws JMSException
  { writeObjectInternal(new Double(value)); }
//...e
//...sString:2:
// from String, and rest via toString()

public String readString()
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    return null;
  if ( o instanceof String )
    return (String) o;
  return o.toString();
  }

public void writeString(String value)
  throws JMSException
  { writeObjectInternal(value); }
//...e
//...sBytes:2:
// from byte[] only

public int readBytes(byte[] value)
  throws JMSException
  {
  Object o = readObject();
  if ( o == null )
    throw new MessageFormatException("null object in stream");
  if ( ! ( o instanceof byte[] ) )
    throw new MessageFormatException(cantConvertObject(o, "byte[]"));
  byte[] b = (byte[]) o;
  if ( bo + value.length <= b.length )
    // We can fully fill the clients buffer, so do so
    {
    System.arraycopy(b, bo, value, 0, value.length);
    bo += value.length;
    --ptr; // step back, so next time we'll look at it again
    return value.length; // value filled, so client must call again
    }
  else if ( bo < b.length )
    // We can partially fill the clients buffer
    {
    System.arraycopy(b, bo, value, 0, b.length-bo);
    bo = 0; // for next time we read another byte[]
    return b.length-bo; // less than value.size(), so client won't call again
    }
  else
    // All data consumed, no more available
    {
    bo = 0; // for next time we read another byte[]
    return -1; // Indicates no more, so client won't call again
    }
  }

public void writeBytes(byte[] value)
  throws JMSException
  { writeBytes(value, 0, value.length); }

public void writeBytes(byte[] value, int offset, int length)
  throws JMSException
  {
  checkWrite();
  byte[] b = new byte[value.length];
  System.arraycopy(value, offset, b, 0, length);
  objs.add(b);
  }
//...e
//...sObject:2:
public Object readObject()
  throws JMSException
  {
  checkRead();
  if ( ptr == objs.size() )
    throw new MessageEOFException("");
  return objs.elementAt(ptr++);
  }

public void writeObject(Object value)
  throws JMSException
  {
  checkWrite();
  if ( value == null )
    // Spec appears ambiguous about whether this is legal
    throw new MessageFormatException("object cannot be null");
  else if ( value instanceof byte[] )
    writeBytes((byte[])value);
  else if ( value instanceof Boolean )
    writeBoolean(((Boolean)value).booleanValue());
  else if ( value instanceof Byte )
    writeByte(((Byte)value).byteValue());
  else if ( value instanceof Character )
    writeChar(((Character)value).charValue());
  else if ( value instanceof Short )
    writeShort(((Short)value).shortValue());
  else if ( value instanceof Integer )
    writeInt(((Integer)value).intValue());
  else if ( value instanceof Long )
    writeLong(((Long)value).longValue());
  else if ( value instanceof Float )
    writeFloat(((Float)value).floatValue());
  else if ( value instanceof Double )
    writeDouble(((Double)value).doubleValue());
  else if ( value instanceof String )
    writeString((String)value);
  else
    throw new MessageFormatException("value is of invalid class: " + value.getClass().getName());
  }

// Non-JMS, but handy
protected void writeObjectInternal(Object value)
  throws JMSException
  {
  checkWrite();
  objs.add(value);
  }
//...e

//...sreset:2:
public void reset()
  {
  ptr = 0;  
  bo  = 0;
  bro = true;
  }
//...e
//...sclearBody:2:
public void clearBody()
  throws JMSException
  {
  objs = new Vector<Object>();
  bro  = false;
  }
//...e

//...sprotect:2:
// Before passing a newly received JmsaMessage to the caller,
// ensure it is not modifiable, as per JMS spec.

public void protect()
  {
  pro = true;
  reset();
  }
//...e

//...sJmsaStreamMessage:2:
// Called by JmsaSession
public JmsaStreamMessage()
  {
  super();
  objs = new Vector<Object>();
  }

// For newly received messages, or foreign outbound messages
public JmsaStreamMessage(StreamMessage m)
  throws JMSException // caused by super(m) and m.readObject()
  {
  super(m);

  objs = new Vector<Object>();
  try
    {
    for ( ;; )
      {
      Object o = m.readObject();
      // Note, I'm trusting the JMS implementation providing
      // m to only return valid Object types, as per spec.
      objs.add(o);
      // Note that I don't (can't) clone the objects,
      // as they're not modified either by the underlying JMS implementation
      // which provided m, or by the methods exposed on this JmsaStreamMessage
      }
    }
  catch ( MessageEOFException e )
    {
    // Offically there is no other way to detect the end of the stream
    }
  ptr = 0;
  bo  = 0;
  bro = true;
  }

// For converting between message formats
public JmsaStreamMessage(JmsaMessage m)
  {
  super(m);
  objs = new Vector<Object>();
  }
//...e
  }
