summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/io/ObjectOutputStream.java
diff options
context:
space:
mode:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2005-07-16 00:30:23 +0000
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>2005-07-16 00:30:23 +0000
commitc8875fb97fc03779a5bba09872227b1d08e5d52a (patch)
treea0b991cf5866ae1d616639b906ac001811d74508 /libjava/classpath/java/io/ObjectOutputStream.java
parentc40c1730800ed292b6db39a83d592476fa59623c (diff)
downloadgcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@102074 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java/io/ObjectOutputStream.java')
-rw-r--r--libjava/classpath/java/io/ObjectOutputStream.java1578
1 files changed, 1578 insertions, 0 deletions
diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java
new file mode 100644
index 00000000000..5e754c5ec7a
--- /dev/null
+++ b/libjava/classpath/java/io/ObjectOutputStream.java
@@ -0,0 +1,1578 @@
+/* ObjectOutputStream.java -- Class used to write serialized objects
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.io;
+
+import gnu.classpath.Configuration;
+import gnu.java.io.ObjectIdentityWrapper;
+import gnu.java.lang.reflect.TypeSignature;
+import gnu.java.security.action.SetAccessibleAction;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.util.Hashtable;
+
+/**
+ * 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, they 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
+ */
+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();
+
+ if (DEBUG)
+ {
+ String val = System.getProperty("gcj.dumpobjects");
+ if (val != null && !val.equals(""))
+ dump = true;
+ }
+ }
+
+ /**
+ * 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,
+ * they 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 InvalidClassException Somebody tried to serialize
+ * an object which is wrongly formatted.
+ *
+ * @exception IOException Exception from underlying
+ * <code>OutputStream</code>.
+ */
+ public final void writeObject(Object obj) throws IOException
+ {
+ if (useSubclassMethod)
+ {
+ if (dump)
+ dumpElementln ("WRITE OVERRIDE: " + obj);
+
+ writeObjectOverride(obj);
+ return;
+ }
+
+ if (dump)
+ dumpElementln ("WRITE: " + obj);
+
+ depth += 2;
+
+ boolean was_serializing = isSerializing;
+ boolean old_mode = setBlockDataMode(false);
+ try
+ {
+ isSerializing = true;
+ boolean replaceDone = false;
+ Object replacedObject = null;
+
+ 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)
+ {
+ Class cl = (Class)obj;
+ ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
+ realOutput.writeByte(TC_CLASS);
+ if (!osc.isProxyClass)
+ {
+ writeObject (osc);
+ }
+ else
+ {
+ realOutput.writeByte(TC_PROXYCLASSDESC);
+ Class[] intfs = cl.getInterfaces();
+ realOutput.writeInt(intfs.length);
+ for (int i = 0; i < intfs.length; i++)
+ realOutput.writeUTF(intfs[i].getName());
+
+ boolean oldmode = setBlockDataMode(true);
+ annotateProxyClass(cl);
+ setBlockDataMode(oldmode);
+ realOutput.writeByte(TC_ENDBLOCKDATA);
+
+ writeObject(osc.getSuper());
+ }
+ assignNewHandle(obj);
+ break;
+ }
+
+ if (obj instanceof ObjectStreamClass)
+ {
+ writeClassDescriptor((ObjectStreamClass) obj);
+ break;
+ }
+
+ Class clazz = obj.getClass();
+ ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
+ if (osc == null)
+ throw new NotSerializableException(clazz.getName());
+
+ if ((replacementEnabled || obj instanceof Serializable)
+ && ! replaceDone)
+ {
+ replacedObject = obj;
+
+ if (obj instanceof Serializable)
+ {
+ try
+ {
+ Method m = osc.writeReplaceMethod;
+ if (m != null)
+ obj = m.invoke(obj, new Object[0]);
+ }
+ catch (IllegalAccessException ignore)
+ {
+ }
+ catch (InvocationTargetException ignore)
+ {
+ }
+ }
+
+ if (replacementEnabled)
+ obj = replaceObject(obj);
+
+ replaceDone = true;
+ continue;
+ }
+
+ if (obj instanceof String)
+ {
+ realOutput.writeByte(TC_STRING);
+ assignNewHandle(obj);
+ realOutput.writeUTF((String)obj);
+ break;
+ }
+
+ if (clazz.isArray ())
+ {
+ realOutput.writeByte(TC_ARRAY);
+ writeObject(osc);
+ assignNewHandle(obj);
+ writeArraySizeAndElements(obj, clazz.getComponentType());
+ 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);
+ realOutput.writeByte(TC_ENDBLOCKDATA);
+ }
+
+ break;
+ }
+
+ if (obj instanceof Serializable)
+ {
+ Object prevObject = this.currentObject;
+ ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
+ currentObject = obj;
+ ObjectStreamClass[] hierarchy =
+ ObjectStreamClass.getObjectStreamClasses(clazz);
+
+ for (int i = 0; i < hierarchy.length; i++)
+ {
+ currentObjectStreamClass = hierarchy[i];
+
+ fieldsAlreadyWritten = false;
+ if (currentObjectStreamClass.hasWriteMethod())
+ {
+ if (dump)
+ dumpElementln ("WRITE METHOD CALLED FOR: " + obj);
+ setBlockDataMode(true);
+ callWriteMethod(obj, currentObjectStreamClass);
+ setBlockDataMode(false);
+ realOutput.writeByte(TC_ENDBLOCKDATA);
+ if (dump)
+ dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj);
+ }
+ else
+ {
+ if (dump)
+ dumpElementln ("WRITE FIELDS CALLED FOR: " + obj);
+ writeFields(obj, currentObjectStreamClass);
+ }
+ }
+
+ this.currentObject = prevObject;
+ this.currentObjectStreamClass = prevObjectStreamClass;
+ currentPutField = null;
+ break;
+ }
+
+ throw new NotSerializableException(clazz.getName ());
+ } // end pseudo-loop
+ }
+ catch (ObjectStreamException ose)
+ {
+ // Rethrow these are fatal.
+ throw ose;
+ }
+ catch (IOException e)
+ {
+ realOutput.writeByte(TC_EXCEPTION);
+ reset(true);
+
+ setBlockDataMode(false);
+ try
+ {
+ if (DEBUG)
+ {
+ e.printStackTrace(System.out);
+ }
+ writeObject(e);
+ }
+ catch (IOException ioe)
+ {
+ StreamCorruptedException ex =
+ new StreamCorruptedException
+ (ioe + " thrown while exception was being written to stream.");
+ if (DEBUG)
+ {
+ ex.printStackTrace(System.out);
+ }
+ throw ex;
+ }
+
+ reset (true);
+
+ }
+ finally
+ {
+ isSerializing = was_serializing;
+ setBlockDataMode(old_mode);
+ depth -= 2;
+
+ if (dump)
+ dumpElementln ("END: " + obj);
+ }
+ }
+
+ protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
+ {
+ realOutput.writeByte(TC_CLASSDESC);
+ realOutput.writeUTF(osc.getName());
+ realOutput.writeLong(osc.getSerialVersionUID());
+ assignNewHandle(osc);
+
+ 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());
+ }
+
+ boolean oldmode = setBlockDataMode(true);
+ annotateClass(osc.forClass());
+ setBlockDataMode(oldmode);
+ realOutput.writeByte(TC_ENDBLOCKDATA);
+
+ if (osc.isSerializable() || osc.isExternalizable())
+ writeObject(osc.getSuper());
+ else
+ writeObject(null);
+ }
+
+ /**
+ * 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);
+ }
+
+
+ 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 writeFields and defaultWriteObject 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_2</code> by default, as is done
+ * by the JDK 1.2.
+ *
+ * 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 ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
+ */
+ protected void annotateClass(Class cl) throws IOException
+ {
+ }
+
+ protected void annotateProxyClass(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)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new SerializablePermission("enableSubstitution"));
+ }
+
+ 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 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 DataOutputStream#write(byte[])
+ */
+ public void write(byte[] b) throws IOException
+ {
+ write(b, 0, b.length);
+ }
+
+
+ /**
+ * @see 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 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;
+
+ if (writeDataAsBlocks)
+ writeBlockDataHeader(blockDataCount);
+ realOutput.write(blockData, 0, blockDataCount);
+ blockDataCount = 0;
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#close ()
+ */
+ public void close() throws IOException
+ {
+ flush();
+ realOutput.close();
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeBoolean (boolean)
+ */
+ public void writeBoolean(boolean data) throws IOException
+ {
+ blockDataOutput.writeBoolean(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeByte (int)
+ */
+ public void writeByte(int data) throws IOException
+ {
+ blockDataOutput.writeByte(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeShort (int)
+ */
+ public void writeShort (int data) throws IOException
+ {
+ blockDataOutput.writeShort(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeChar (int)
+ */
+ public void writeChar(int data) throws IOException
+ {
+ blockDataOutput.writeChar(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeInt (int)
+ */
+ public void writeInt(int data) throws IOException
+ {
+ blockDataOutput.writeInt(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeLong (long)
+ */
+ public void writeLong(long data) throws IOException
+ {
+ blockDataOutput.writeLong(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeFloat (float)
+ */
+ public void writeFloat(float data) throws IOException
+ {
+ blockDataOutput.writeFloat(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeDouble (double)
+ */
+ public void writeDouble(double data) throws IOException
+ {
+ blockDataOutput.writeDouble(data);
+ }
+
+
+ /**
+ * @see java.io.DataOutputStream#writeBytes (java.lang.String)
+ */
+ public void writeBytes(String data) throws IOException
+ {
+ blockDataOutput.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 abstract static class PutField
+ {
+ public abstract void put (String name, boolean value);
+ public abstract void put (String name, byte value);
+ public abstract void put (String name, char value);
+ public abstract void put (String name, double value);
+ public abstract void put (String name, float value);
+ public abstract void put (String name, int value);
+ public abstract void put (String name, long value);
+ public abstract void put (String name, short value);
+ public abstract void put (String name, Object value);
+
+ /**
+ * @deprecated
+ */
+ public abstract void write (ObjectOutput out) throws IOException;
+ }
+
+ public PutField putFields() throws IOException
+ {
+ if (currentPutField != null)
+ return currentPutField;
+
+ currentPutField = new PutField()
+ {
+ private byte[] prim_field_data
+ = new byte[currentObjectStreamClass.primFieldSize];
+ private Object[] objs
+ = new Object[currentObjectStreamClass.objectFieldCount];
+
+ private ObjectStreamField getField (String name)
+ {
+ ObjectStreamField field
+ = currentObjectStreamClass.getField(name);
+
+ if (field == null)
+ throw new IllegalArgumentException("no such serializable field " + name);
+
+ return field;
+ }
+
+ public void put(String name, boolean value)
+ {
+ ObjectStreamField field = getField(name);
+
+ checkType(field, 'Z');
+ prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
+ }
+
+ public void put(String name, byte value)
+ {
+ ObjectStreamField field = getField(name);
+
+ checkType(field, 'B');
+ prim_field_data[field.getOffset()] = value;
+ }
+
+ public void put(String name, char value)
+ {
+ ObjectStreamField field = getField(name);
+
+ checkType(field, 'C');
+ int off = field.getOffset();
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put(String name, double value)
+ {
+ ObjectStreamField field = getField (name);
+
+ checkType(field, 'D');
+ 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)
+ {
+ ObjectStreamField field = getField(name);
+
+ checkType(field, 'F');
+ 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)
+ {
+ ObjectStreamField field = getField(name);
+ checkType(field, 'I');
+ 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)
+ {
+ ObjectStreamField field = getField(name);
+ checkType(field, 'J');
+ 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)
+ {
+ ObjectStreamField field = getField(name);
+ checkType(field, 'S');
+ int off = field.getOffset();
+ prim_field_data[off++] = (byte)(value >>> 8);
+ prim_field_data[off] = (byte)value;
+ }
+
+ public void put(String name, Object value)
+ {
+ ObjectStreamField field = getField(name);
+
+ if (value != null &&
+ ! field.getType().isAssignableFrom(value.getClass ()))
+ throw new IllegalArgumentException("Class " + value.getClass() +
+ " cannot be cast to " + field.getType());
+ objs[field.getOffset()] = value;
+ }
+
+ public void write(ObjectOutput out) throws IOException
+ {
+ // Apparently Block data is not used with PutField as per
+ // empirical evidence against JDK 1.2. Also see Mauve test
+ // java.io.ObjectInputOutput.Test.GetPutField.
+ boolean oldmode = setBlockDataMode(false);
+ out.write(prim_field_data);
+ for (int i = 0; i < objs.length; ++ i)
+ out.writeObject(objs[i]);
+ setBlockDataMode(oldmode);
+ }
+
+ 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");
+
+ markFieldsWritten();
+ 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);
+ realOutput.write(cast_array, 0, length);
+ 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 for the specified ObjectStreamClass.
+ // FIELDS are already in canonical order.
+ private void writeFields(Object obj, ObjectStreamClass osc)
+ throws IOException
+ {
+ ObjectStreamField[] fields = osc.fields;
+ boolean oldmode = setBlockDataMode(false);
+ String field_name;
+ Class type;
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ field_name = fields[i].getName();
+ type = fields[i].getType();
+
+ if (dump)
+ dumpElementln ("WRITE FIELD: " + field_name + " type=" + type);
+
+ if (type == Boolean.TYPE)
+ realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name));
+ else if (type == Byte.TYPE)
+ realOutput.writeByte(getByteField(obj, osc.forClass(), field_name));
+ else if (type == Character.TYPE)
+ realOutput.writeChar(getCharField(obj, osc.forClass(), field_name));
+ else if (type == Double.TYPE)
+ realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name));
+ else if (type == Float.TYPE)
+ realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name));
+ else if (type == Integer.TYPE)
+ realOutput.writeInt(getIntField(obj, osc.forClass(), field_name));
+ else if (type == Long.TYPE)
+ realOutput.writeLong(getLongField(obj, osc.forClass(), field_name));
+ else if (type == Short.TYPE)
+ realOutput.writeShort(getShortField(obj, osc.forClass(), field_name));
+ else
+ writeObject(getObjectField(obj, osc.forClass(), field_name,
+ fields[i].getTypeString ()));
+ }
+ setBlockDataMode(oldmode);
+ }
+
+
+ // Toggles writing primitive data to block-data buffer.
+ // Package-private to avoid a trampoline constructor.
+ boolean setBlockDataMode(boolean on) throws IOException
+ {
+ if (on == writeDataAsBlocks)
+ return on;
+
+ drain();
+ boolean oldmode = writeDataAsBlocks;
+ writeDataAsBlocks = on;
+
+ if (on)
+ dataOutput = blockDataOutput;
+ else
+ dataOutput = realOutput;
+
+ return oldmode;
+ }
+
+
+ private void callWriteMethod(Object obj, ObjectStreamClass osc)
+ throws IOException
+ {
+ currentPutField = null;
+ try
+ {
+ Object args[] = {this};
+ osc.writeObjectMethod.invoke(obj, args);
+ }
+ catch (InvocationTargetException x)
+ {
+ /* Rethrow if possible. */
+ Throwable exception = x.getTargetException();
+ if (exception instanceof RuntimeException)
+ throw (RuntimeException) exception;
+ if (exception instanceof IOException)
+ throw (IOException) exception;
+
+ IOException ioe
+ = new IOException("Exception thrown from writeObject() on " +
+ osc.forClass().getName() + ": " +
+ exception.getClass().getName());
+ ioe.initCause(exception);
+ throw ioe;
+ }
+ catch (Exception x)
+ {
+ IOException ioe
+ = new IOException("Failure invoking writeObject() on " +
+ osc.forClass().getName() + ": " +
+ x.getClass().getName());
+ ioe.initCause(x);
+ throw ioe;
+ }
+ }
+
+ private boolean getBooleanField(Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField(klass, field_name);
+ boolean b = f.getBoolean(obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private byte getByteField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ byte b = f.getByte (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private char getCharField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ char b = f.getChar (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private double getDoubleField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ double b = f.getDouble (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private float getFloatField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ float b = f.getFloat (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private int getIntField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ int b = f.getInt (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private long getLongField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ long b = f.getLong (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private short getShortField (Object obj, Class klass, String field_name)
+ throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ short b = f.getShort (obj);
+ return b;
+ }
+ catch (IllegalArgumentException _)
+ {
+ throw new InvalidClassException
+ ("invalid requested type for field " + field_name + " in class " + klass.getName());
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception _)
+ {
+ throw new IOException("Unexpected exception " + _);
+ }
+ }
+
+ private Object getObjectField (Object obj, Class klass, String field_name,
+ String type_code) throws IOException
+ {
+ try
+ {
+ Field f = getField (klass, field_name);
+ ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType());
+
+ /* if of is primitive something went wrong
+ * in the check for primitive classes in writeFields.
+ */
+ if (of.isPrimitive())
+ throw new InvalidClassException
+ ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive");
+
+ if (!of.getTypeString().equals(type_code))
+ throw new InvalidClassException
+ ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code);
+
+ Object o = f.get (obj);
+ // FIXME: We should check the type_code here
+ return o;
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new IOException ();
+ }
+ }
+
+ private Field getField (Class klass, String name)
+ throws java.io.InvalidClassException
+ {
+ try
+ {
+ final Field f = klass.getDeclaredField(name);
+ setAccessible.setMember(f);
+ AccessController.doPrivileged(setAccessible);
+ return f;
+ }
+ catch (java.lang.NoSuchFieldException e)
+ {
+ throw new InvalidClassException
+ ("no field called " + name + " in class " + klass.getName());
+ }
+ }
+
+ private void dumpElementln (String msg)
+ {
+ for (int i = 0; i < depth; i++)
+ System.out.print (" ");
+ System.out.print (Thread.currentThread() + ": ");
+ System.out.println(msg);
+ }
+
+ // this value comes from 1.2 spec, but is used in 1.1 as well
+ private static final int BUFFER_SIZE = 1024;
+
+ private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
+
+ private DataOutputStream dataOutput;
+ private boolean writeDataAsBlocks;
+ private DataOutputStream realOutput;
+ private DataOutputStream blockDataOutput;
+ private byte[] blockData;
+ private int blockDataCount;
+ private Object currentObject;
+ // Package-private to avoid a trampoline.
+ 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;
+ private SetAccessibleAction setAccessible = new SetAccessibleAction();
+
+ // The nesting depth for debugging output
+ private int depth = 0;
+
+ // Set if we're generating debugging dumps
+ private boolean dump = false;
+
+ private static final boolean DEBUG = false;
+
+ static
+ {
+ if (Configuration.INIT_LOAD_LIBRARY)
+ {
+ System.loadLibrary("javaio");
+ }
+ }
+}