aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/io/ObjectOutputStream.java
diff options
context:
space:
mode:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2000-05-19 17:55:34 +0000
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2000-05-19 17:55:34 +0000
commit51acace4cce1255942e3e3bd3a7ecbff7f5b9377 (patch)
tree88cf0d32aea197ea8e8198e1206b04c820308615 /libjava/java/io/ObjectOutputStream.java
parent6e95df84d1be816955443d07c4935189b0341c63 (diff)
Jumbo patch:
* Imported beans and serialization * Updated IA-64 port * Miscellaneous bug fixes git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@34028 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/java/io/ObjectOutputStream.java')
-rw-r--r--libjava/java/io/ObjectOutputStream.java1335
1 files changed, 1335 insertions, 0 deletions
diff --git a/libjava/java/io/ObjectOutputStream.java b/libjava/java/io/ObjectOutputStream.java
new file mode 100644
index 00000000000..a98b55baf19
--- /dev/null
+++ b/libjava/java/io/ObjectOutputStream.java
@@ -0,0 +1,1335 @@
+/* ObjectOutputStream.java -- Class used to write serialized objects
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+
+package java.io;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+
+import gnu.java.io.ObjectIdentityWrapper;
+import gnu.java.lang.reflect.TypeSignature;
+
+/**
+ An <code>ObjectOutputStream</code> can be used to write objects
+ as well as primitive data in a platform-independent manner to an
+ <code>OutputStream</code>.
+
+ The data produced by an <code>ObjectOutputStream</code> can be read
+ and reconstituted by an <code>ObjectInputStream</code>.
+
+ <code>writeObject (Object)</code> is used to write Objects, the
+ <code>write&lt;type&gt;</code> methods are used to write primitive
+ data (as in <code>DataOutputStream</code>). Strings can be written
+ as objects or as primitive data.
+
+ Not all objects can be written out using an
+ <code>ObjectOutputStream</code>. Only those objects that are an
+ instance of <code>java.io.Serializable</code> can be written.
+
+ Using default serialization, information about the class of an
+ object is written, all of the non-transient, non-static fields of
+ the object are written, if any of these fields are objects, the are
+ written out in the same manner.
+
+ An object is only written out the first time it is encountered. If
+ the object is encountered later, a reference to it is written to
+ the underlying stream. Thus writing circular object graphs
+ does not present a problem, nor are relationships between objects
+ in a graph lost.
+
+ Example usage:
+ <pre>
+ Hashtable map = new Hashtable ();
+ map.put ("one", new Integer (1));
+ map.put ("two", new Integer (2));
+
+ ObjectOutputStream oos =
+ new ObjectOutputStream (new FileOutputStream ("numbers"));
+ oos.writeObject (map);
+ oos.close ();
+
+ ObjectInputStream ois =
+ new ObjectInputStream (new FileInputStream ("numbers"));
+ Hashtable newmap = (Hashtable)ois.readObject ();
+
+ System.out.println (newmap);
+ </pre>
+
+ The default serialization can be overriden in two ways.
+
+ By defining a method <code>private void
+ writeObject (ObjectOutputStream)</code>, a class can dictate exactly
+ how information about itself is written.
+ <code>defaultWriteObject ()</code> may be called from this method to
+ carry out default serialization. This method is not
+ responsible for dealing with fields of super-classes or subclasses.
+
+ By implementing <code>java.io.Externalizable</code>. This gives
+ the class complete control over the way it is written to the
+ stream. If this approach is used the burden of writing superclass
+ and subclass data is transfered to the class implementing
+ <code>java.io.Externalizable</code>.
+
+ @see java.io.DataOutputStream
+ @see java.io.Externalizable
+ @see java.io.ObjectInputStream
+ @see java.io.Serializable
+ @see XXX: java serialization spec
+*/
+public class ObjectOutputStream extends OutputStream
+ implements ObjectOutput, ObjectStreamConstants
+{
+ /**
+ Creates a new <code>ObjectOutputStream</code> that will do all of
+ its writing onto <code>out</code>. This method also initializes
+ the stream by writing the header information (stream magic number
+ and stream version).
+
+ @exception IOException Writing stream header to underlying
+ stream cannot be completed.
+
+ @see writeStreamHeader ()
+ */
+ public ObjectOutputStream (OutputStream out) throws IOException
+ {
+ realOutput = new DataOutputStream (out);
+ blockData = new byte[ BUFFER_SIZE ];
+ blockDataCount = 0;
+ blockDataOutput = new DataOutputStream (this);
+ setBlockDataMode (true);
+ replacementEnabled = false;
+ isSerializing = false;
+ nextOID = baseWireHandle;
+ OIDLookupTable = new Hashtable ();
+ protocolVersion = defaultProtocolVersion;
+ useSubclassMethod = false;
+ writeStreamHeader ();
+ }
+
+
+ /**
+ Writes a representation of <code>obj</code> to the underlying
+ output stream by writing out information about its class, then
+ writing out each of the objects non-transient, non-static
+ fields. If any of these fields are other objects,
+ the are written out in the same manner.
+
+ This method can be overriden by a class by implementing
+ <code>private void writeObject (ObjectOutputStream)</code>.
+
+ If an exception is thrown from this method, the stream is left in
+ an undefined state.
+
+ @exception NotSerializableException An attempt was made to
+ serialize an <code>Object</code> that is not serializable.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+ */
+ public final void writeObject (Object obj) throws IOException
+ {
+ if (useSubclassMethod)
+ {
+ writeObjectOverride (obj);
+ return;
+ }
+
+ boolean was_serializing = isSerializing;
+
+ if (! was_serializing)
+ setBlockDataMode (false);
+
+ try
+ {
+ isSerializing = true;
+ boolean replaceDone = false;
+
+ drain ();
+
+ while (true)
+ {
+ if (obj == null)
+ {
+ realOutput.writeByte (TC_NULL);
+ break;
+ }
+
+ Integer handle = findHandle (obj);
+ if (handle != null)
+ {
+ realOutput.writeByte (TC_REFERENCE);
+ realOutput.writeInt (handle.intValue ());
+ break;
+ }
+
+ if (obj instanceof Class)
+ {
+ realOutput.writeByte (TC_CLASS);
+ writeObject (ObjectStreamClass.lookup ((Class)obj));
+ assignNewHandle (obj);
+ break;
+ }
+
+ if (obj instanceof ObjectStreamClass)
+ {
+ ObjectStreamClass osc = (ObjectStreamClass)obj;
+ realOutput.writeByte (TC_CLASSDESC);
+ realOutput.writeUTF (osc.getName ());
+ realOutput.writeLong (osc.getSerialVersionUID ());
+ assignNewHandle (obj);
+
+ int flags = osc.getFlags ();
+
+ if (protocolVersion == PROTOCOL_VERSION_2
+ && osc.isExternalizable ())
+ flags |= SC_BLOCK_DATA;
+
+ realOutput.writeByte (flags);
+
+ ObjectStreamField[] fields = osc.fields;
+ realOutput.writeShort (fields.length);
+
+ ObjectStreamField field;
+ for (int i=0; i < fields.length; i++)
+ {
+ field = fields[i];
+ realOutput.writeByte (field.getTypeCode ());
+ realOutput.writeUTF (field.getName ());
+
+ if (! field.isPrimitive ())
+ writeObject (field.getTypeString ());
+ }
+
+ setBlockDataMode (true);
+ annotateClass (osc.forClass ());
+ setBlockDataMode (false);
+ realOutput.writeByte (TC_ENDBLOCKDATA);
+
+ if (osc.isSerializable ())
+ writeObject (osc.getSuper ());
+ else
+ writeObject (null);
+ break;
+ }
+
+
+ Object replacedObject = null;
+
+ if ((replacementEnabled || obj instanceof Replaceable)
+ && ! replaceDone)
+ {
+ replacedObject = obj;
+
+ if (obj instanceof Replaceable)
+ obj = ((Replaceable)obj).writeReplace ();
+
+ if (replacementEnabled)
+ obj = replaceObject (obj);
+
+ replaceDone = true;
+ continue;
+ }
+
+ if (obj instanceof String)
+ {
+ realOutput.writeByte (TC_STRING);
+ assignNewHandle (obj);
+ realOutput.writeUTF ((String)obj);
+ break;
+ }
+
+ Class clazz = obj.getClass ();
+ ObjectStreamClass osc = ObjectStreamClass.lookup (clazz);
+ if (osc == null)
+ throw new NotSerializableException ("The class "
+ + clazz.getName ()
+ + " is not Serializable");
+
+ if (clazz.isArray ())
+ {
+ realOutput.writeByte (TC_ARRAY);
+ writeObject (osc);
+ assignNewHandle (obj);
+ writeArraySizeAndElements (obj, clazz);
+ break;
+ }
+
+ realOutput.writeByte (TC_OBJECT);
+ writeObject (osc);
+
+ if (replaceDone)
+ assignNewHandle (replacedObject);
+ else
+ assignNewHandle (obj);
+
+ if (obj instanceof Externalizable)
+ {
+ if (protocolVersion == PROTOCOL_VERSION_2)
+ setBlockDataMode (true);
+
+ ((Externalizable)obj).writeExternal (this);
+
+ if (protocolVersion == PROTOCOL_VERSION_2)
+ {
+ setBlockDataMode (false);
+ drain ();
+ }
+
+ break;
+ }
+
+ if (obj instanceof Serializable)
+ {
+ currentObject = obj;
+ ObjectStreamClass[] hierarchy =
+ ObjectStreamClass.getObjectStreamClasses (clazz);
+
+ boolean has_write;
+ for (int i=0; i < hierarchy.length; i++)
+ {
+ currentObjectStreamClass = hierarchy[i];
+
+ has_write = currentObjectStreamClass.hasWriteMethod ();
+ writeFields (obj, currentObjectStreamClass.fields,
+ has_write);
+
+ fieldsAlreadyWritten = false;
+
+ if (has_write)
+ {
+ drain ();
+ realOutput.writeByte (TC_ENDBLOCKDATA);
+ }
+ }
+
+ currentObject = null;
+ currentObjectStreamClass = null;
+ currentPutField = null;
+ break;
+ }
+
+ throw new NotSerializableException ("Instances of the class "
+ + clazz.getName ()
+ + " are not Serializable");
+ } // end pseudo-loop
+ }
+ catch (IOException e)
+ {
+ realOutput.writeByte (TC_EXCEPTION);
+ reset (true);
+
+ try
+ {
+ writeObject (e);
+ }
+ catch (IOException ioe)
+ {
+ throw new StreamCorruptedException ("Exception " + ioe + " thrown while exception was being written to stream.");
+ }
+
+ reset (true);
+ }
+ finally
+ {
+ isSerializing = was_serializing;
+
+ if (! was_serializing)
+ setBlockDataMode (true);
+ }
+ }
+
+
+ /**
+ Writes the current objects non-transient, non-static fields from
+ the current class to the underlying output stream.
+
+ This method is intended to be called from within a object's
+ <code>private void writeObject (ObjectOutputStream)</code>
+ method.
+
+ @exception NotActiveException This method was called from a
+ context other than from the current object's and current class's
+ <code>private void writeObject (ObjectOutputStream)</code>
+ method.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+ */
+ public void defaultWriteObject ()
+ throws IOException, NotActiveException
+ {
+ markFieldsWritten ();
+ writeFields (currentObject, currentObjectStreamClass.fields, false);
+ }
+
+
+ private void markFieldsWritten () throws IOException
+ {
+ if (currentObject == null || currentObjectStreamClass == null)
+ throw new NotActiveException ("defaultWriteObject called by non-active class and/or object");
+
+ if (fieldsAlreadyWritten)
+ throw new IOException ("Only one of putFields and defalutWriteObject may be called, and it may only be called once");
+
+ fieldsAlreadyWritten = true;
+ }
+
+
+ /**
+ Resets stream to state equivalent to the state just after it was
+ constructed.
+
+ Causes all objects previously written to the stream to be
+ forgotten. A notification of this reset is also written to the
+ underlying stream.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code> or reset called while serialization is
+ in progress.
+ */
+ public void reset () throws IOException
+ {
+ reset (false);
+ }
+
+
+ private void reset (boolean internal) throws IOException
+ {
+ if (!internal)
+ {
+ if (isSerializing)
+ throw new IOException ("Reset called while serialization in progress");
+
+ realOutput.writeByte (TC_RESET);
+ }
+
+ clearHandles ();
+ }
+
+
+ /**
+ Informs this <code>ObjectOutputStream</code> to write data
+ according to the specified protocol. There are currently two
+ different protocols, specified by <code>PROTOCOL_VERSION_1</code>
+ and <code>PROTOCOL_VERSION_2</code>. This implementation writes
+ data using <code>PROTOCOL_VERSION_1</code> by default, as is done
+ by the JDK 1.1.
+
+ A non-portable method, <code>setDefaultProtocolVersion (int
+ version)</code> is provided to change the default protocol
+ version.
+
+ For an explination of the differences beween the two protocols
+ see XXX: the Java ObjectSerialization Specification.
+
+ @exception IOException if <code>version</code> is not a valid
+ protocol
+
+ @see setDefaultProtocolVersion (int)
+ */
+ public void useProtocolVersion (int version) throws IOException
+ {
+ if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
+ throw new IOException ("Invalid protocol version requested.");
+
+ protocolVersion = version;
+ }
+
+
+ /**
+ <em>GNU $classpath specific</em>
+
+ Changes the default stream protocol used by all
+ <code>ObjectOutputStream</code>s. There are currently two
+ different protocols, specified by <code>PROTOCOL_VERSION_1</code>
+ and <code>PROTOCOL_VERSION_2</code>. The default default is
+ <code>PROTOCOL_VERSION_1</code>.
+
+ @exception IOException if <code>version</code> is not a valid
+ protocol
+
+ @see useProtocolVersion (int)
+ */
+ public static void setDefaultProtocolVersion (int version)
+ throws IOException
+ {
+ if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
+ throw new IOException ("Invalid protocol version requested.");
+
+ defaultProtocolVersion = version;
+ }
+
+
+ /**
+ An empty hook that allows subclasses to write extra information
+ about classes to the stream. This method is called the first
+ time each class is seen, and after all of the standard
+ information about the class has been written.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+
+ @see java.io.ObjectInputStream#resolveClass (java.io.ObjectStreamClass)
+ */
+ protected void annotateClass (Class cl) throws IOException
+ {}
+
+
+ /**
+ Allows subclasses to replace objects that are written to the
+ stream with other objects to be written in their place. This
+ method is called the first time each object is encountered
+ (modulo reseting of the stream).
+
+ This method must be enabled before it will be called in the
+ serialization process.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+
+ @see enableReplaceObject (boolean)
+ */
+ protected Object replaceObject (Object obj) throws IOException
+ {
+ return obj;
+ }
+
+
+ /**
+ If <code>enable</code> is <code>true</code> and this object is
+ trusted, then <code>replaceObject (Object)</code> will be called
+ in subsequent calls to <code>writeObject (Object)</code>.
+ Otherwise, <code>replaceObject (Object)</code> will not be called.
+
+ @exception SecurityException This class is not trusted.
+ */
+ protected boolean enableReplaceObject (boolean enable)
+ throws SecurityException
+ {
+ if (enable)
+ if (getClass ().getClassLoader () != null)
+ throw new SecurityException ("Untrusted ObjectOutputStream subclass attempted to enable object replacement");
+
+ boolean old_val = replacementEnabled;
+ replacementEnabled = enable;
+ return old_val;
+ }
+
+
+ /**
+ Writes stream magic and stream version information to the
+ underlying stream.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+ */
+ protected void writeStreamHeader () throws IOException
+ {
+ realOutput.writeShort (STREAM_MAGIC);
+ realOutput.writeShort (STREAM_VERSION);
+ }
+
+
+
+ /**
+ Protected constructor that allows subclasses to override
+ serialization. This constructor should be called by subclasses
+ that wish to override <code>writeObject (Object)</code>. This
+ method does a security check <i>NOTE: currently not
+ implemented</i>, then sets a flag that informs
+ <code>writeObject (Object)</code> to call the subclasses
+ <code>writeObjectOverride (Object)</code> method.
+
+ @see writeObjectOverride (Object)
+ */
+ protected ObjectOutputStream () throws IOException, SecurityException
+ {
+ SecurityManager sec_man = System.getSecurityManager ();
+ if (sec_man != null)
+ sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
+ useSubclassMethod = true;
+ }
+
+
+ /**
+ This method allows subclasses to override the default
+ serialization mechanism provided by
+ <code>ObjectOutputStream</code>. To make this method be used for
+ writing objects, subclasses must invoke the 0-argument
+ constructor on this class from there constructor.
+
+ @see ObjectOutputStream ()
+
+ @exception NotActiveException Subclass has arranged for this
+ method to be called, but did not implement this method.
+ */
+ protected void writeObjectOverride (Object obj) throws NotActiveException,
+ IOException
+ {
+ throw new NotActiveException ("Subclass of ObjectOutputStream must implement writeObjectOverride");
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#write (int)
+ */
+ public void write (int data) throws IOException
+ {
+ if (writeDataAsBlocks)
+ {
+ if (blockDataCount == BUFFER_SIZE)
+ drain ();
+
+ blockData[ blockDataCount++ ] = (byte)data;
+ }
+ else
+ realOutput.write (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#write (byte[])
+ */
+ public void write (byte b[]) throws IOException
+ {
+ write (b, 0, b.length);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#write (byte[],int,int)
+ */
+ public void write (byte b[], int off, int len) throws IOException
+ {
+ if (writeDataAsBlocks)
+ {
+ if (len < 0)
+ throw new IndexOutOfBoundsException ();
+
+ if (blockDataCount + len < BUFFER_SIZE)
+ {
+ System.arraycopy (b, off, blockData, blockDataCount, len);
+ blockDataCount += len;
+ }
+ else
+ {
+ drain ();
+ writeBlockDataHeader (len);
+ realOutput.write (b, off, len);
+ }
+ }
+ else
+ realOutput.write (b, off, len);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#flush ()
+ */
+ public void flush () throws IOException
+ {
+ drain ();
+ realOutput.flush ();
+ }
+
+
+ /**
+ Causes the block-data buffer to be written to the underlying
+ stream, but does not flush underlying stream.
+
+ @exception IOException Exception from underlying
+ <code>OutputStream</code>.
+ */
+ protected void drain () throws IOException
+ {
+ if (blockDataCount == 0)
+ return;
+
+ writeBlockDataHeader (blockDataCount);
+ realOutput.write (blockData, 0, blockDataCount);
+ blockDataCount = 0;
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#close ()
+ */
+ public void close () throws IOException
+ {
+ drain ();
+ realOutput.close ();
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeBoolean (boolean)
+ */
+ public void writeBoolean (boolean data) throws IOException
+ {
+ dataOutput.writeBoolean (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeByte (int)
+ */
+ public void writeByte (int data) throws IOException
+ {
+ dataOutput.writeByte (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeShort (int)
+ */
+ public void writeShort (int data) throws IOException
+ {
+ dataOutput.writeShort (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeChar (int)
+ */
+ public void writeChar (int data) throws IOException
+ {
+ dataOutput.writeChar (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeInt (int)
+ */
+ public void writeInt (int data) throws IOException
+ {
+ dataOutput.writeInt (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeLong (long)
+ */
+ public void writeLong (long data) throws IOException
+ {
+ dataOutput.writeLong (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeFloat (float)
+ */
+ public void writeFloat (float data) throws IOException
+ {
+ dataOutput.writeFloat (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeDouble (double)
+ */
+ public void writeDouble (double data) throws IOException
+ {
+ dataOutput.writeDouble (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeBytes (java.lang.String)
+ */
+ public void writeBytes (String data) throws IOException
+ {
+ dataOutput.writeBytes (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeChars (java.lang.String)
+ */
+ public void writeChars (String data) throws IOException
+ {
+ dataOutput.writeChars (data);
+ }
+
+
+ /**
+ @see java.io.DataOutputStream#writeUTF (java.lang.String)
+ */
+ public void writeUTF (String data) throws IOException
+ {
+ dataOutput.writeUTF (data);
+ }
+
+
+ /**
+ This class allows a class to specify exactly which fields should
+ be written, and what values should be written for these fields.
+
+ XXX: finish up comments
+ */
+ public static abstract class PutField
+ {
+ public abstract void put (String name, boolean value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, byte value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, char value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, double value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, float value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, int value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, long value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, short value)
+ throws IOException, IllegalArgumentException;
+ public abstract void put (String name, Object value)
+ throws IOException, IllegalArgumentException;
+ public abstract void write (ObjectOutput out) throws IOException;
+ }
+
+
+ public PutField putFields () throws IOException
+ {
+ markFieldsWritten ();
+
+ currentPutField = new PutField ()
+ {
+ private byte[] prim_field_data
+ = new byte[currentObjectStreamClass.primFieldSize];
+ private Object[] objs
+ = new Object[currentObjectStreamClass.objectFieldCount];
+
+ public void put (String name, boolean value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'Z');
+ prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
+ }
+
+ public void put (String name, byte value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ prim_field_data[field.getOffset ()] = value;
+ }
+
+ public void put (String name, char value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put (String name, double value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ long l_value = Double.doubleToLongBits (value);
+ prim_field_data[off++] = (byte)(l_value >>> 52);
+ prim_field_data[off++] = (byte)(l_value >>> 48);
+ prim_field_data[off++] = (byte)(l_value >>> 40);
+ prim_field_data[off++] = (byte)(l_value >>> 32);
+ prim_field_data[off++] = (byte)(l_value >>> 24);
+ prim_field_data[off++] = (byte)(l_value >>> 16);
+ prim_field_data[off++] = (byte)(l_value >>> 8);
+ prim_field_data[off] = (byte)l_value;
+ }
+
+ public void put (String name, float value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ int i_value = Float.floatToIntBits (value);
+ prim_field_data[off++] = (byte)(i_value >>> 24);
+ prim_field_data[off++] = (byte)(i_value >>> 16);
+ prim_field_data[off++] = (byte)(i_value >>> 8);
+ prim_field_data[off] = (byte)i_value;
+ }
+
+ public void put (String name, int value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ prim_field_data[off++] = (byte)(value >>> 24);
+ prim_field_data[off++] = (byte)(value >>> 16);
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put (String name, long value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ prim_field_data[off++] = (byte)(value >>> 52);
+ prim_field_data[off++] = (byte)(value >>> 48);
+ prim_field_data[off++] = (byte)(value >>> 40);
+ prim_field_data[off++] = (byte)(value >>> 32);
+ prim_field_data[off++] = (byte)(value >>> 24);
+ prim_field_data[off++] = (byte)(value >>> 16);
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put (String name, short value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ checkType (field, 'B');
+ int off = field.getOffset ();
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put (String name, Object value)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField (name);
+ if (! field.getType ().isAssignableFrom (value.getClass ()))
+ throw new IllegalArgumentException ();
+ objs[field.getOffset ()] = value;
+ }
+
+ public void write (ObjectOutput out) throws IOException
+ {
+ out.write (prim_field_data);
+ for (int i = 0; i < objs.length; ++ i)
+ out.writeObject (objs[i]);
+ }
+
+ private void checkType (ObjectStreamField field, char type)
+ throws IllegalArgumentException
+ {
+ if (TypeSignature.getEncodingOfClass (field.getType ()).charAt (0) != type)
+ throw new IllegalArgumentException ();
+ }
+ };
+ // end PutFieldImpl
+
+ return currentPutField;
+ }
+
+
+ public void writeFields () throws IOException
+ {
+ if (currentPutField == null)
+ throw new NotActiveException ("writeFields can only be called after putFields has been called");
+
+ currentPutField.write (this);
+ }
+
+
+ // write out the block-data buffer, picking the correct header
+ // depending on the size of the buffer
+ private void writeBlockDataHeader (int size) throws IOException
+ {
+ if (size < 256)
+ {
+ realOutput.writeByte (TC_BLOCKDATA);
+ realOutput.write (size);
+ }
+ else
+ {
+ realOutput.writeByte (TC_BLOCKDATALONG);
+ realOutput.writeInt (size);
+ }
+ }
+
+
+ // lookup the handle for OBJ, return null if OBJ doesn't have a
+ // handle yet
+ private Integer findHandle (Object obj)
+ {
+ return (Integer)OIDLookupTable.get (new ObjectIdentityWrapper (obj));
+ }
+
+
+ // assigns the next availible handle to OBJ
+ private int assignNewHandle (Object obj)
+ {
+ OIDLookupTable.put (new ObjectIdentityWrapper (obj),
+ new Integer (nextOID));
+ return nextOID++;
+ }
+
+
+ // resets mapping from objects to handles
+ private void clearHandles ()
+ {
+ nextOID = baseWireHandle;
+ OIDLookupTable.clear ();
+ }
+
+
+ // write out array size followed by each element of the array
+ private void writeArraySizeAndElements (Object array, Class clazz)
+ throws IOException
+ {
+ int length = Array.getLength (array);
+
+ if (clazz.isPrimitive ())
+ {
+ if (clazz == Boolean.TYPE)
+ {
+ boolean[] cast_array = (boolean[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeBoolean (cast_array[i]);
+ return;
+ }
+ if (clazz == Byte.TYPE)
+ {
+ byte[] cast_array = (byte[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeByte (cast_array[i]);
+ return;
+ }
+ if (clazz == Character.TYPE)
+ {
+ char[] cast_array = (char[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeChar (cast_array[i]);
+ return;
+ }
+ if (clazz == Double.TYPE)
+ {
+ double[] cast_array = (double[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeDouble (cast_array[i]);
+ return;
+ }
+ if (clazz == Float.TYPE)
+ {
+ float[] cast_array = (float[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeFloat (cast_array[i]);
+ return;
+ }
+ if (clazz == Integer.TYPE)
+ {
+ int[] cast_array = (int[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeInt (cast_array[i]);
+ return;
+ }
+ if (clazz == Long.TYPE)
+ {
+ long[] cast_array = (long[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeLong (cast_array[i]);
+ return;
+ }
+ if (clazz == Short.TYPE)
+ {
+ short[] cast_array = (short[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ realOutput.writeShort (cast_array[i]);
+ return;
+ }
+ }
+ else
+ {
+ Object[] cast_array = (Object[])array;
+ realOutput.writeInt (length);
+ for (int i=0; i < length; i++)
+ writeObject (cast_array[i]);
+ }
+ }
+
+
+ // writes out FIELDS of OBJECT. If CALL_WRITE_METHOD is true, use
+ // object's writeObject (ObjectOutputStream), otherwise use default
+ // serialization. FIELDS are already in canonical order.
+ private void writeFields (Object obj,
+ ObjectStreamField[] fields,
+ boolean call_write_method) throws IOException
+ {
+ if (call_write_method)
+ {
+ setBlockDataMode (true);
+ callWriteMethod (obj);
+ setBlockDataMode (false);
+ return;
+ }
+
+ String field_name;
+ Class type;
+ for (int i=0; i < fields.length; i++)
+ {
+ field_name = fields[i].getName ();
+ type = fields[i].getType ();
+
+ if (type == Boolean.TYPE)
+ realOutput.writeBoolean (getBooleanField (obj, field_name));
+ else if (type == Byte.TYPE)
+ realOutput.writeByte (getByteField (obj, field_name));
+ else if (type == Character.TYPE)
+ realOutput.writeChar (getCharField (obj, field_name));
+ else if (type == Double.TYPE)
+ realOutput.writeDouble (getDoubleField (obj, field_name));
+ else if (type == Float.TYPE)
+ realOutput.writeFloat (getFloatField (obj, field_name));
+ else if (type == Integer.TYPE)
+ realOutput.writeInt (getIntField (obj, field_name));
+ else if (type == Long.TYPE)
+ realOutput.writeLong (getLongField (obj, field_name));
+ else if (type == Short.TYPE)
+ realOutput.writeShort (getShortField (obj, field_name));
+ else
+ writeObject (getObjectField (obj, field_name,
+ TypeSignature.getEncodingOfClass (type)));
+ }
+ }
+
+
+ // Toggles writing primitive data to block-data buffer.
+ private void setBlockDataMode (boolean on)
+ {
+ writeDataAsBlocks = on;
+
+ if (on)
+ dataOutput = blockDataOutput;
+ else
+ dataOutput = realOutput;
+ }
+
+
+ private void callWriteMethod (Object obj) throws IOException
+ {
+ try
+ {
+ Class classArgs[] = {Class.forName ("java.io.ObjectOutputStream")};
+ Class klass = obj.getClass ();
+ Method m = getMethod (klass, "writeObject", classArgs);
+ if (m == null)
+ return;
+ Object args[] = {this};
+ m.invoke (obj, args);
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private boolean getBooleanField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ boolean b = f.getBoolean (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private byte getByteField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ byte b = f.getByte (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private char getCharField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ char b = f.getChar (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private double getDoubleField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ double b = f.getDouble (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private float getFloatField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ float b = f.getFloat (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private int getIntField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ int b = f.getInt (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private long getLongField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ long b = f.getLong (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private short getShortField (Object obj, String field_name) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ short b = f.getShort (obj);
+ return b;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private Object getObjectField (Object obj, String field_name,
+ String type_code) throws IOException
+ {
+ try
+ {
+ Class klass = obj.getClass ();
+ Field f = getField (klass, field_name);
+ Object o = f.get (obj);
+ // FIXME: We should check the type_code here
+ return o;
+ }
+ catch (Exception _)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private static native Field getField (Class klass, String name)
+ throws java.lang.NoSuchFieldException;
+
+ private static native Method getMethod (Class klass, String name, Class args[])
+ throws java.lang.NoSuchMethodException;
+
+ // this value comes from 1.2 spec, but is used in 1.1 as well
+ private final static int BUFFER_SIZE = 1024;
+
+ private static int defaultProtocolVersion = PROTOCOL_VERSION_1;
+
+ private DataOutputStream dataOutput;
+ private boolean writeDataAsBlocks;
+ private DataOutputStream realOutput;
+ private DataOutputStream blockDataOutput;
+ private byte[] blockData;
+ private int blockDataCount;
+ private Object currentObject;
+ private ObjectStreamClass currentObjectStreamClass;
+ private PutField currentPutField;
+ private boolean fieldsAlreadyWritten;
+ private boolean replacementEnabled;
+ private boolean isSerializing;
+ private int nextOID;
+ private Hashtable OIDLookupTable;
+ private int protocolVersion;
+ private boolean useSubclassMethod;
+}