| /* ObjectOutputStream.java -- Class used to write serialized objects | |
| Copyright (C) 1998, 1999, 2000 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.lang.reflect.InvocationTargetException; | |
| 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<type></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 | |
| @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, | |
| 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 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 Serializable) | |
| && ! replaceDone) | |
| { | |
| replacedObject = obj; | |
| if (obj instanceof Serializable) | |
| { | |
| Method m = null; | |
| try | |
| { | |
| Class classArgs[] = {}; | |
| m = obj.getClass ().getDeclaredMethod ("writeReplace", | |
| classArgs); | |
| // m can't be null by definition since an exception would | |
| // have been thrown so a check for null is not needed. | |
| obj = m.invoke (obj, new Object[] {}); | |
| } | |
| catch (NoSuchMethodException ignore) | |
| { | |
| } | |
| 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; | |
| } | |
| Class clazz = obj.getClass (); | |
| ObjectStreamClass osc = ObjectStreamClass.lookup (clazz); | |
| if (osc == null) | |
| throw new NotSerializableException (clazz.getName ()); | |
| 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); | |
| 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]; | |
| fieldsAlreadyWritten = false; | |
| has_write = currentObjectStreamClass.hasWriteMethod (); | |
| writeFields (obj, currentObjectStreamClass.fields, | |
| has_write); | |
| if (has_write) | |
| { | |
| drain (); | |
| realOutput.writeByte (TC_ENDBLOCKDATA); | |
| } | |
| } | |
| currentObject = null; | |
| currentObjectStreamClass = null; | |
| currentPutField = null; | |
| break; | |
| } | |
| throw new NotSerializableException (clazz.getName ()); | |
| } // 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 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_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, '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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.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) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field | |
| = currentObjectStreamClass.getField (name); | |
| if (value != null && | |
| ! field.getType ().isAssignableFrom (value.getClass ())) | |
| throw new IllegalArgumentException (); | |
| 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. | |
| setBlockDataMode (false); | |
| out.write (prim_field_data); | |
| for (int i = 0; i < objs.length; ++ i) | |
| out.writeObject (objs[i]); | |
| setBlockDataMode (true); | |
| } | |
| 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 | |
| { | |
| Class klass = obj.getClass (); | |
| try | |
| { | |
| Class classArgs[] = {ObjectOutputStream.class}; | |
| Method m = getMethod (klass, "writeObject", classArgs); | |
| if (m == null) | |
| return; | |
| Object args[] = {this}; | |
| m.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; | |
| throw new IOException ("Exception thrown from writeObject() on " + | |
| klass + ": " + exception.getClass().getName()); | |
| } | |
| catch (Exception x) | |
| { | |
| throw new IOException ("Failure invoking writeObject() on " + | |
| klass + ": " + x.getClass().getName()); | |
| } | |
| } | |
| 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; | |
| } |