/*
 * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

/*
 * $Id$
 */

package com.sun.ts.tests.jms.common;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;

import jakarta.jms.JMSException;
import jakarta.jms.MessageEOFException;
import jakarta.jms.MessageFormatException;
import jakarta.jms.MessageNotReadableException;
import jakarta.jms.MessageNotWriteableException;
import jakarta.jms.StreamMessage;

/**
 * Class Declaration.
 *
 * @version 1.2, 09/26/00
 */
public class StreamMessageTestImpl extends MessageTestImpl implements StreamMessage {
    static final private ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("buf", byte[].class) };

    // every write method will first write the type byte,
    // and then the data with that type.
    public static final byte BOOLEAN_TYPE = 1;

    public static final byte BYTE_TYPE = 2;

    public static final byte CHAR_TYPE = 3;

    public static final byte DOUBLE_TYPE = 4;

    public static final byte FLOAT_TYPE = 5;

    public static final byte INT_TYPE = 6;

    public static final byte LONG_TYPE = 7;

    public static final byte SHORT_TYPE = 8;

    public static final byte STRING_TYPE = 9;

    public static final byte BYTES_TYPE = 10;

    byte[] buf = new byte[0];

    transient ByteArrayInputStream bais;

    transient ByteArrayOutputStream baos;

    transient DataInputStream dis;

    transient DataOutputStream dos;

    // for read/writeBytes
    private boolean first_time_readBytes = true;

    private int available_bytes = 0;

    /**
     * Class Constructor.
     * 
     * 
     * @see
     */
    public StreamMessageTestImpl() {
        super();
        init();
    } // StreamMessageTestImpl()

    /**
     * Method Declaration.
     * 
     * 
     * @see
     */
    private void init() {
        baos = new ByteArrayOutputStream();
        dos = new DataOutputStream(baos);
    }

    /**
     * Method Declaration.
     * 
     * 
     * @param oos
     *
     * @exception IOException
     *
     * @see
     */
    private void writeObject(ObjectOutputStream oos) throws IOException {
        dos.flush();
        buf = baos.toByteArray();
        oos.defaultWriteObject();
    }

    /**
     * Method Declaration.
     * 
     * 
     * @param ois
     *
     * @exception ClassNotFoundException
     * @exception IOException
     *
     * @see
     */
    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
        ois.defaultReadObject();
        baos = new ByteArrayOutputStream();
        dos = new DataOutputStream(baos);
        if (buf != null) {
            dos.write(buf);
            buf = null;
        }
    }

    /**
     * Method Declaration.
     * 
     * 
     * @return
     * 
     * @exception JMSException
     * 
     * @see
     */
    private byte getType() throws IOException {
        return dis.readByte();
    } // getType()

    /**
     * Method Declaration.
     * 
     * 
     * @return
     *
     * @exception IOException
     *
     * @see
     */
    private int getBytesLength() throws IOException {
        return dis.readInt();
    } // getType()

    /**
     * Read a <code>boolean</code> from the stream message.
     * 
     * @return the <code>boolean</code> value read.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public boolean readBoolean() throws JMSException {
        boolean ret = false;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BOOLEAN_TYPE:
                ret = dis.readBoolean();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Boolean.valueOf(s).booleanValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readBoolean()

    /**
     * Read a byte value from the stream message.
     * 
     * @return the next byte from the stream message as a 8-bit <code>byte</code>.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public byte readByte() throws JMSException {
        byte ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BYTE_TYPE:
                ret = dis.readByte();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Byte.valueOf(s).byteValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readByte()

    /**
     * Read a 16-bit number from the stream message.
     * 
     * @return a 16-bit number from the stream message.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public short readShort() throws JMSException {
        short ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BYTE_TYPE:
                ret = dis.readByte();
                break;
            case SHORT_TYPE:
                ret = dis.readShort();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Short.valueOf(s).shortValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readShort()

    /**
     * Read a Unicode character value from the stream message.
     * 
     * @return a Unicode character from the stream message.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public char readChar() throws JMSException {
        char ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case CHAR_TYPE:
                ret = dis.readChar();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = s.charAt(0); // ???
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readChar()

    /**
     * Read a 32-bit integer from the stream message.
     * 
     * @return a 32-bit integer value from the stream message, interpreted as a <code>int</code>.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public int readInt() throws JMSException {
        int ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BYTE_TYPE:
                ret = dis.readByte();
                break;
            case SHORT_TYPE:
                ret = dis.readShort();
                break;
            case INT_TYPE:
                ret = dis.readInt();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Integer.valueOf(s).intValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readInt()

    /**
     * Read a 64-bit integer from the stream message.
     * 
     * @return a 64-bit integer value from the stream message, interpreted as a <code>long</code>.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public long readLong() throws JMSException {
        long ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BYTE_TYPE:
                ret = dis.readByte();
                break;
            case SHORT_TYPE:
                ret = dis.readShort();
                break;
            case INT_TYPE:
                ret = dis.readInt();
                break;
            case LONG_TYPE:
                ret = dis.readLong();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Long.valueOf(s).longValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readLong()

    /**
     * Read a <code>float</code> from the stream message.
     * 
     * @return a <code>float</code> value from the stream message.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public float readFloat() throws JMSException {
        float ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case FLOAT_TYPE:
                ret = dis.readFloat();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Float.valueOf(s).floatValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readFloat()

    /**
     * Read a <code>double</code> from the stream message.
     * 
     * @return a <code>double</code> value from the stream message.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public double readDouble() throws JMSException {
        double ret = 0;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case FLOAT_TYPE:
                ret = dis.readFloat();
                break;
            case DOUBLE_TYPE:
                ret = dis.readDouble();
                break;
            case STRING_TYPE:
                String s = dis.readUTF();

                ret = Double.valueOf(s).doubleValue();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readDouble()

    /**
     * Read in a string from the stream message.
     * 
     * @return a Unicode string from the stream message.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public String readString() throws JMSException {
        String ret = null;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BOOLEAN_TYPE:
                ret = String.valueOf(dis.readBoolean());
                break;
            case BYTE_TYPE:
                ret = String.valueOf(dis.readByte());
                break;
            case SHORT_TYPE:
                ret = String.valueOf(dis.readShort());
                break;
            case CHAR_TYPE:
                ret = String.valueOf(dis.readChar());
                break;
            case INT_TYPE:
                ret = String.valueOf(dis.readInt());
                break;
            case LONG_TYPE:
                ret = String.valueOf(dis.readLong());
                break;
            case FLOAT_TYPE:
                ret = String.valueOf(dis.readFloat());
                break;
            case DOUBLE_TYPE:
                ret = String.valueOf(dis.readDouble());
                break;
            case STRING_TYPE:
                ret = dis.readUTF();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readString()

    /**
     * Read a byte array from the stream message.
     * 
     * @param value the buffer into which the data is read.
     * 
     * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream
     * has been reached.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageFormatException if this type conversion is invalid
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public int readBytes(byte[] value) throws JMSException {
        int ret = -1;
        checkReadAccess();

        try {
            byte type = BYTES_TYPE;

            // get the type and the length of this bytes array field
            // if this is the first time read this bytes array field
            if (first_time_readBytes) {
                type = getType();
                available_bytes = getBytesLength();
            }
            switch (type) {
            case BYTES_TYPE:

                // bytes array field is empty
                if (first_time_readBytes && available_bytes == 0) {
                    return 0;
                } else if (!first_time_readBytes && available_bytes == 0) {

                    /*
                     * this is the case that last time readBytes() read exactly same bytes left in the bytes array field, spec requires an
                     * extra readBytes(), and return -1 ;-(
                     */
                    return -1;
                }
                if (value.length > available_bytes) {

                    // read all! (available_bytes won't be zero.)
                    ret = dis.read(value, 0, available_bytes);
                    available_bytes = 0;

                    // initiate first_time_readBytes to true for next field
                    first_time_readBytes = true;
                } else if (value.length <= available_bytes) {

                    // read all, but needs readBytes again
                    ret = dis.read(value, 0, value.length);
                    available_bytes = available_bytes - value.length;
                    first_time_readBytes = false;
                }
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readBytes()

    /**
     * Read a Java object from the stream message.
     * 
     *
     * Note that this method can be used to return in objectified format, an object that had been written to the Stream with
     * the equivalent <CODE>writeObject</CODE> method call, or it's equivalent primitive write<type> method.
     * 
     * @return a Java object from the stream message, in objectified format (ie. if it set as an int, then a Integer is
     * returned).
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageEOFException if an end of message stream
     * @exception MessageNotReadableException if message in write-only mode.
     */
    public Object readObject() throws JMSException {
        Object ret = null;
        checkReadAccess();

        try {
            byte type = getType();

            switch (type) {
            case BOOLEAN_TYPE:
                ret = Boolean.valueOf(dis.readBoolean());
                break;
            case BYTE_TYPE:
                ret = Byte.valueOf(dis.readByte());
                break;
            case SHORT_TYPE:
                ret = Short.valueOf(dis.readShort());
                break;
            case CHAR_TYPE:
                ret = Character.valueOf(dis.readChar());
                break;
            case INT_TYPE:
                ret = Integer.valueOf(dis.readInt());
                break;
            case LONG_TYPE:
                ret = Long.valueOf(dis.readLong());
                break;
            case FLOAT_TYPE:
                ret = Float.valueOf(dis.readFloat());
                break;
            case DOUBLE_TYPE:
                ret = Double.valueOf(dis.readDouble());
                break;
            case STRING_TYPE:
                ret = dis.readUTF();
                break;
            default:
                throw new MessageFormatException("type conversion is invalid");
            } // switch
        } catch (EOFException e1) {
            throw new MessageEOFException("at end of message"); // I18N
        } catch (IOException e2) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e2);
            throw jmsEx;
        } // try .. catch
        return ret;
    } // readObject()

    /**
     * Write a <code>boolean</code> to the stream message. The value <code>true</code> is written out as the value
     * <code>(byte)1</code>; the value <code>false</code> is written out as the value <code>(byte)0</code>.
     * 
     * @param value the <code>boolean</code> value to be written.
     * 
     * @exception JMSException if JMS fails to read message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeBoolean(boolean value) throws JMSException {
        try {
            dos.writeByte((int) BOOLEAN_TYPE);
            dos.writeBoolean(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeBoolean()

    /**
     * Write out a <code>byte</code> to the stream message.
     * 
     * @param value the <code>byte</code> value to be written.
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeByte(byte value) throws JMSException {
        try {
            dos.writeByte((int) BYTE_TYPE);
            dos.writeByte((int) value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeByte()

    /**
     * Write a <code>short</code> to the stream message.
     * 
     * @param value the <code>short</code> to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeShort(short value) throws JMSException {
        try {
            dos.writeByte((int) SHORT_TYPE);
            dos.writeShort((int) value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeShort()

    /**
     * Write a <code>char</code> to the stream message.
     * 
     * @param value the <code>char</code> value to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeChar(char value) throws JMSException {
        try {
            dos.writeByte((int) CHAR_TYPE);
            dos.writeChar((int) value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeChar()

    /**
     * Write an <code>int</code> to the stream message.
     * 
     * @param value the <code>int</code> to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeInt(int value) throws JMSException {
        try {
            dos.writeByte((int) INT_TYPE);
            dos.writeInt(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeInt()

    /**
     * Write a <code>long</code> to the stream message.
     * 
     * @param value the <code>long</code> to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeLong(long value) throws JMSException {
        try {
            dos.writeByte((int) LONG_TYPE);
            dos.writeLong(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeLong()

    /**
     * Write a <code>float</code> to the stream message.
     * 
     * @param value the <code>float</code> value to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeFloat(float value) throws JMSException {
        try {
            dos.writeByte((int) FLOAT_TYPE);
            dos.writeFloat(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeFloat()

    /**
     * Write a <code>double</code> to the stream message.
     * 
     * @param value the <code>double</code> value to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeDouble(double value) throws JMSException {
        try {
            dos.writeByte((int) DOUBLE_TYPE);
            dos.writeDouble(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeDouble()

    /**
     * Write a string to the stream message.
     * 
     * @param value the <code>String</code> value to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeString(String value) throws JMSException {
        try {
            dos.writeByte((int) STRING_TYPE);
            dos.writeUTF(value);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeString()

    /**
     * Write a byte array to the stream message.
     * 
     * @param value the byte array to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeBytes(byte[] value) throws JMSException {
        writeBytes(value, 0, value.length);
    } // writeBytes()

    /**
     * Write a portion of a byte array to the stream message.
     * 
     * @param value the byte array value to be written.
     * @param offset the initial offset within the byte array.
     * @param length the number of bytes to use.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     */
    public void writeBytes(byte[] value, int offset, int length) throws JMSException {

        /*
         * bytes array field format as following: TYPE LENGTH DATA
         */
        try {
            dos.writeByte((int) BYTES_TYPE);
            dos.writeInt(length);
            dos.write(value, offset, length);
            setBufferIsDirty(true);
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
    } // writeBytes()

    /**
     * Write a Java object to the stream message.
     * 
     *
     * Note that this method only works for the objectified primitive object types (Integer, Double, Long ...), String's and
     * byte arrays.
     * 
     * @param value the Java object to be written.
     * 
     * @exception JMSException if JMS fails to write message due to some internal JMS error.
     * @exception MessageNotWriteableException if message in read-only mode.
     * @exception MessageFormatException if the object is invalid
     */
    public void writeObject(Object value) throws JMSException {
        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 Double) {
            writeDouble(((Double) value).doubleValue());
        } else if (value instanceof Float) {
            writeFloat(((Float) value).floatValue());
        } else if (value instanceof Integer) {
            writeInt(((Integer) value).intValue());
        } else if (value instanceof Long) {
            writeLong(((Long) value).longValue());
        } else if (value instanceof Short) {
            writeShort(((Short) value).shortValue());
        } else if (value instanceof String) {
            writeString((String) value);
        } else if (value instanceof byte[]) {
            writeBytes((byte[]) value);
        } else {
            throw new MessageFormatException("Invalid type"); // I18N
        } // if .. else
    } // writeObject()

    /**
     * Put the message in read-only mode, and reposition the stream to the beginning.
     * 
     * @exception JMSException if JMS fails to reset the message due to some internal JMS error.
     * @exception MessageFormatException if message has an invalid format
     */
    public void reset() throws JMSException {

        // forces any buffered output bytes to be written out to the stream
        // not really needed in this case, because the underlying output stream
        // is a ByteArrayOutputStream
        try {
            if (bufferIsDirty) {
                dos.flush();
                dos.close();
                baos.close();
            }
        } catch (IOException e) {
            JMSException jmsEx = new JMSException("IOException"); // I18N

            jmsEx.setLinkedException(e);
            throw jmsEx;
        } // try .. catch
        if (baos != null) {

            // copy the content of DataOutputStream dos to buf
            buf = baos.toByteArray();

        } else {
            if (buf == null) {
                buf = new byte[0];
            }
        }
        bais = new ByteArrayInputStream(buf);
        dis = new DataInputStream(bais);

        // initiate first_time_readBytes to true for readBytes()
        first_time_readBytes = true;
        setBufferIsDirty(false);
        readMode = true;
    } // reset()

    // overwrite methods in MessageImpl

    /**
     * Method Declaration.
     * 
     * 
     * @exception JMSException
     * 
     * @see
     */
    public void clearBody() throws JMSException {
        buf = null;
        bais = null;
        dis = null;
        readMode = false;
    } // clearBody()

}
