2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / io / ObjectOutputStream.java
blob0244e2d2c915c0e48458717a5b7728e7fd6e40bf
1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.io;
41 import java.lang.reflect.Array;
42 import java.lang.reflect.Field;
43 import java.lang.reflect.Method;
44 import java.lang.reflect.InvocationTargetException;
45 import java.security.PrivilegedAction;
46 import java.security.AccessController;
47 import java.util.Hashtable;
49 import gnu.java.io.ObjectIdentityWrapper;
50 import gnu.java.lang.reflect.TypeSignature;
51 import gnu.classpath.Configuration;
53 /**
54 * An <code>ObjectOutputStream</code> can be used to write objects
55 * as well as primitive data in a platform-independent manner to an
56 * <code>OutputStream</code>.
58 * The data produced by an <code>ObjectOutputStream</code> can be read
59 * and reconstituted by an <code>ObjectInputStream</code>.
61 * <code>writeObject (Object)</code> is used to write Objects, the
62 * <code>write&lt;type&gt;</code> methods are used to write primitive
63 * data (as in <code>DataOutputStream</code>). Strings can be written
64 * as objects or as primitive data.
66 * Not all objects can be written out using an
67 * <code>ObjectOutputStream</code>. Only those objects that are an
68 * instance of <code>java.io.Serializable</code> can be written.
70 * Using default serialization, information about the class of an
71 * object is written, all of the non-transient, non-static fields of
72 * the object are written, if any of these fields are objects, they are
73 * written out in the same manner.
75 * An object is only written out the first time it is encountered. If
76 * the object is encountered later, a reference to it is written to
77 * the underlying stream. Thus writing circular object graphs
78 * does not present a problem, nor are relationships between objects
79 * in a graph lost.
81 * Example usage:
82 * <pre>
83 * Hashtable map = new Hashtable ();
84 * map.put ("one", new Integer (1));
85 * map.put ("two", new Integer (2));
87 * ObjectOutputStream oos =
88 * new ObjectOutputStream (new FileOutputStream ("numbers"));
89 * oos.writeObject (map);
90 * oos.close ();
92 * ObjectInputStream ois =
93 * new ObjectInputStream (new FileInputStream ("numbers"));
94 * Hashtable newmap = (Hashtable)ois.readObject ();
96 * System.out.println (newmap);
97 * </pre>
99 * The default serialization can be overriden in two ways.
101 * By defining a method <code>private void
102 * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
103 * how information about itself is written.
104 * <code>defaultWriteObject ()</code> may be called from this method to
105 * carry out default serialization. This method is not
106 * responsible for dealing with fields of super-classes or subclasses.
108 * By implementing <code>java.io.Externalizable</code>. This gives
109 * the class complete control over the way it is written to the
110 * stream. If this approach is used the burden of writing superclass
111 * and subclass data is transfered to the class implementing
112 * <code>java.io.Externalizable</code>.
114 * @see java.io.DataOutputStream
115 * @see java.io.Externalizable
116 * @see java.io.ObjectInputStream
117 * @see java.io.Serializable
119 public class ObjectOutputStream extends OutputStream
120 implements ObjectOutput, ObjectStreamConstants
123 * Creates a new <code>ObjectOutputStream</code> that will do all of
124 * its writing onto <code>out</code>. This method also initializes
125 * the stream by writing the header information (stream magic number
126 * and stream version).
128 * @exception IOException Writing stream header to underlying
129 * stream cannot be completed.
131 * @see #writeStreamHeader()
133 public ObjectOutputStream (OutputStream out) throws IOException
135 realOutput = new DataOutputStream (out);
136 blockData = new byte[ BUFFER_SIZE ];
137 blockDataCount = 0;
138 blockDataOutput = new DataOutputStream (this);
139 setBlockDataMode (true);
140 replacementEnabled = false;
141 isSerializing = false;
142 nextOID = baseWireHandle;
143 OIDLookupTable = new Hashtable ();
144 protocolVersion = defaultProtocolVersion;
145 useSubclassMethod = false;
146 writeStreamHeader ();
150 * Writes a representation of <code>obj</code> to the underlying
151 * output stream by writing out information about its class, then
152 * writing out each of the objects non-transient, non-static
153 * fields. If any of these fields are other objects,
154 * they are written out in the same manner.
156 * This method can be overriden by a class by implementing
157 * <code>private void writeObject (ObjectOutputStream)</code>.
159 * If an exception is thrown from this method, the stream is left in
160 * an undefined state.
162 * @exception NotSerializableException An attempt was made to
163 * serialize an <code>Object</code> that is not serializable.
165 * @exception IOException Exception from underlying
166 * <code>OutputStream</code>.
168 public final void writeObject (Object obj) throws IOException
170 if (useSubclassMethod)
172 writeObjectOverride (obj);
173 return;
176 boolean was_serializing = isSerializing;
177 boolean old_mode = setBlockDataMode (false);
180 isSerializing = true;
181 boolean replaceDone = false;
182 Object replacedObject = null;
184 while (true)
186 if (obj == null)
188 realOutput.writeByte (TC_NULL);
189 break;
192 Integer handle = findHandle (obj);
193 if (handle != null)
195 realOutput.writeByte (TC_REFERENCE);
196 realOutput.writeInt (handle.intValue ());
197 break;
200 if (obj instanceof Class)
202 Class cl = (Class)obj;
203 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (cl);
204 assignNewHandle (obj);
205 realOutput.writeByte (TC_CLASS);
206 if (!osc.isProxyClass)
208 writeObject (osc);
210 else
212 realOutput.writeByte (TC_PROXYCLASSDESC);
213 Class[] intfs = cl.getInterfaces();
214 realOutput.writeInt(intfs.length);
215 for (int i = 0; i < intfs.length; i++)
216 realOutput.writeUTF(intfs[i].getName());
218 boolean oldmode = setBlockDataMode (true);
219 annotateProxyClass(cl);
220 setBlockDataMode (oldmode);
221 realOutput.writeByte(TC_ENDBLOCKDATA);
223 writeObject (osc.getSuper());
225 break;
228 if (obj instanceof ObjectStreamClass)
230 writeClassDescriptor ((ObjectStreamClass) obj);
231 break;
234 if ((replacementEnabled || obj instanceof Serializable)
235 && ! replaceDone)
237 replacedObject = obj;
239 if (obj instanceof Serializable)
241 Method m = null;
244 Class classArgs[] = {};
245 m = getMethod(obj.getClass(), "writeReplace",
246 classArgs);
247 // m can't be null by definition since an
248 // exception would have been thrown so a check
249 // for null is not needed.
250 obj = m.invoke (obj, new Object[] {});
252 catch (NoSuchMethodException ignore)
255 catch (IllegalAccessException ignore)
258 catch (InvocationTargetException ignore)
263 if (replacementEnabled)
264 obj = replaceObject (obj);
266 replaceDone = true;
267 continue;
270 if (obj instanceof String)
272 realOutput.writeByte (TC_STRING);
273 assignNewHandle (obj);
274 realOutput.writeUTF ((String)obj);
275 break;
278 Class clazz = obj.getClass ();
279 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (clazz);
280 if (osc == null)
281 throw new NotSerializableException (clazz.getName ());
283 if (clazz.isArray ())
285 realOutput.writeByte (TC_ARRAY);
286 writeObject (osc);
287 assignNewHandle (obj);
288 writeArraySizeAndElements (obj, clazz.getComponentType ());
289 break;
292 realOutput.writeByte (TC_OBJECT);
293 writeObject (osc);
295 if (replaceDone)
296 assignNewHandle (replacedObject);
297 else
298 assignNewHandle (obj);
300 if (obj instanceof Externalizable)
302 if (protocolVersion == PROTOCOL_VERSION_2)
303 setBlockDataMode (true);
305 ((Externalizable)obj).writeExternal (this);
307 if (protocolVersion == PROTOCOL_VERSION_2)
309 setBlockDataMode (false);
310 realOutput.writeByte (TC_ENDBLOCKDATA);
313 break;
316 if (obj instanceof Serializable)
318 currentObject = obj;
319 ObjectStreamClass[] hierarchy =
320 ObjectStreamClass.getObjectStreamClasses (clazz);
322 for (int i=0; i < hierarchy.length; i++)
324 currentObjectStreamClass = hierarchy[i];
326 fieldsAlreadyWritten = false;
327 if (currentObjectStreamClass.hasWriteMethod ())
329 setBlockDataMode (true);
330 callWriteMethod (obj, currentObjectStreamClass);
331 setBlockDataMode (false);
332 realOutput.writeByte (TC_ENDBLOCKDATA);
334 else
335 writeFields (obj, currentObjectStreamClass);
338 currentObject = null;
339 currentObjectStreamClass = null;
340 currentPutField = null;
341 break;
344 throw new NotSerializableException (clazz.getName ());
345 } // end pseudo-loop
347 catch (ObjectStreamException ose)
349 // Rethrow these are fatal.
350 throw ose;
352 catch (IOException e)
354 realOutput.writeByte (TC_EXCEPTION);
355 reset (true);
357 setBlockDataMode (false);
360 writeObject (e);
362 catch (IOException ioe)
364 throw new StreamCorruptedException ("Exception " + ioe + " thrown while exception ("+e+") was being written to stream.");
367 reset (true);
369 finally
371 isSerializing = was_serializing;
372 setBlockDataMode (old_mode);
376 protected void writeClassDescriptor (ObjectStreamClass osc) throws IOException
378 realOutput.writeByte (TC_CLASSDESC);
379 realOutput.writeUTF (osc.getName ());
380 realOutput.writeLong (osc.getSerialVersionUID ());
381 assignNewHandle (osc);
383 int flags = osc.getFlags ();
385 if (protocolVersion == PROTOCOL_VERSION_2
386 && osc.isExternalizable ())
387 flags |= SC_BLOCK_DATA;
389 realOutput.writeByte (flags);
391 ObjectStreamField[] fields = osc.fields;
392 realOutput.writeShort (fields.length);
394 ObjectStreamField field;
395 for (int i=0; i < fields.length; i++)
397 field = fields[i];
398 realOutput.writeByte (field.getTypeCode ());
399 realOutput.writeUTF (field.getName ());
401 if (! field.isPrimitive ())
402 writeObject (field.getTypeString ());
405 boolean oldmode = setBlockDataMode (true);
406 annotateClass (osc.forClass ());
407 setBlockDataMode (oldmode);
408 realOutput.writeByte (TC_ENDBLOCKDATA);
410 if (osc.isSerializable()
411 || osc.isExternalizable())
412 writeObject (osc.getSuper ());
413 else
414 writeObject (null);
418 Writes the current objects non-transient, non-static fields from
419 the current class to the underlying output stream.
421 This method is intended to be called from within a object's
422 <code>private void writeObject (ObjectOutputStream)</code>
423 method.
425 @exception NotActiveException This method was called from a
426 context other than from the current object's and current class's
427 <code>private void writeObject (ObjectOutputStream)</code>
428 method.
430 @exception IOException Exception from underlying
431 <code>OutputStream</code>.
433 public void defaultWriteObject ()
434 throws IOException, NotActiveException
436 markFieldsWritten ();
437 writeFields (currentObject, currentObjectStreamClass);
441 private void markFieldsWritten () throws IOException
443 if (currentObject == null || currentObjectStreamClass == null)
444 throw new NotActiveException ("defaultWriteObject called by non-active class and/or object");
446 if (fieldsAlreadyWritten)
447 throw new IOException ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
449 fieldsAlreadyWritten = true;
454 Resets stream to state equivalent to the state just after it was
455 constructed.
457 Causes all objects previously written to the stream to be
458 forgotten. A notification of this reset is also written to the
459 underlying stream.
461 @exception IOException Exception from underlying
462 <code>OutputStream</code> or reset called while serialization is
463 in progress.
465 public void reset () throws IOException
467 reset (false);
471 private void reset (boolean internal) throws IOException
473 if (!internal)
475 if (isSerializing)
476 throw new IOException ("Reset called while serialization in progress");
478 realOutput.writeByte (TC_RESET);
481 clearHandles ();
486 Informs this <code>ObjectOutputStream</code> to write data
487 according to the specified protocol. There are currently two
488 different protocols, specified by <code>PROTOCOL_VERSION_1</code>
489 and <code>PROTOCOL_VERSION_2</code>. This implementation writes
490 data using <code>PROTOCOL_VERSION_2</code> by default, as is done
491 by the JDK 1.2.
493 A non-portable method, <code>setDefaultProtocolVersion (int
494 version)</code> is provided to change the default protocol
495 version.
497 For an explination of the differences beween the two protocols
498 see XXX: the Java ObjectSerialization Specification.
500 @exception IOException if <code>version</code> is not a valid
501 protocol
503 @see #setDefaultProtocolVersion(int)
505 public void useProtocolVersion (int version) throws IOException
507 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
508 throw new IOException ("Invalid protocol version requested.");
510 protocolVersion = version;
515 <em>GNU $classpath specific</em>
517 Changes the default stream protocol used by all
518 <code>ObjectOutputStream</code>s. There are currently two
519 different protocols, specified by <code>PROTOCOL_VERSION_1</code>
520 and <code>PROTOCOL_VERSION_2</code>. The default default is
521 <code>PROTOCOL_VERSION_1</code>.
523 @exception IOException if <code>version</code> is not a valid
524 protocol
526 @see #useProtocolVersion(int)
528 public static void setDefaultProtocolVersion (int version)
529 throws IOException
531 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
532 throw new IOException ("Invalid protocol version requested.");
534 defaultProtocolVersion = version;
539 An empty hook that allows subclasses to write extra information
540 about classes to the stream. This method is called the first
541 time each class is seen, and after all of the standard
542 information about the class has been written.
544 @exception IOException Exception from underlying
545 <code>OutputStream</code>.
547 @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
549 protected void annotateClass (Class cl) throws IOException
552 protected void annotateProxyClass(Class cl) throws IOException
556 Allows subclasses to replace objects that are written to the
557 stream with other objects to be written in their place. This
558 method is called the first time each object is encountered
559 (modulo reseting of the stream).
561 This method must be enabled before it will be called in the
562 serialization process.
564 @exception IOException Exception from underlying
565 <code>OutputStream</code>.
567 @see #enableReplaceObject(boolean)
569 protected Object replaceObject (Object obj) throws IOException
571 return obj;
576 If <code>enable</code> is <code>true</code> and this object is
577 trusted, then <code>replaceObject (Object)</code> will be called
578 in subsequent calls to <code>writeObject (Object)</code>.
579 Otherwise, <code>replaceObject (Object)</code> will not be called.
581 @exception SecurityException This class is not trusted.
583 protected boolean enableReplaceObject (boolean enable)
584 throws SecurityException
586 if (enable)
588 SecurityManager sm = System.getSecurityManager ();
589 if (sm != null)
590 sm.checkPermission (new SerializablePermission ("enableSubstitution"));
593 boolean old_val = replacementEnabled;
594 replacementEnabled = enable;
595 return old_val;
600 Writes stream magic and stream version information to the
601 underlying stream.
603 @exception IOException Exception from underlying
604 <code>OutputStream</code>.
606 protected void writeStreamHeader () throws IOException
608 realOutput.writeShort (STREAM_MAGIC);
609 realOutput.writeShort (STREAM_VERSION);
615 * Protected constructor that allows subclasses to override
616 * serialization. This constructor should be called by subclasses
617 * that wish to override <code>writeObject (Object)</code>. This
618 * method does a security check <i>NOTE: currently not
619 * implemented</i>, then sets a flag that informs
620 * <code>writeObject (Object)</code> to call the subclasses
621 * <code>writeObjectOverride (Object)</code> method.
623 * @see #writeObjectOverride(Object)
625 protected ObjectOutputStream () throws IOException, SecurityException
627 SecurityManager sec_man = System.getSecurityManager ();
628 if (sec_man != null)
629 sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
630 useSubclassMethod = true;
635 * This method allows subclasses to override the default
636 * serialization mechanism provided by
637 * <code>ObjectOutputStream</code>. To make this method be used for
638 * writing objects, subclasses must invoke the 0-argument
639 * constructor on this class from there constructor.
641 * @see #ObjectOutputStream()
643 * @exception NotActiveException Subclass has arranged for this
644 * method to be called, but did not implement this method.
646 protected void writeObjectOverride (Object obj) throws NotActiveException,
647 IOException
649 throw new NotActiveException ("Subclass of ObjectOutputStream must implement writeObjectOverride");
654 * @see DataOutputStream#write(int)
656 public void write (int data) throws IOException
658 if (writeDataAsBlocks)
660 if (blockDataCount == BUFFER_SIZE)
661 drain ();
663 blockData[ blockDataCount++ ] = (byte)data;
665 else
666 realOutput.write (data);
671 * @see DataOutputStream#write(byte[])
673 public void write (byte[] b) throws IOException
675 write (b, 0, b.length);
680 * @see DataOutputStream#write(byte[],int,int)
682 public void write (byte[] b, int off, int len) throws IOException
684 if (writeDataAsBlocks)
686 if (len < 0)
687 throw new IndexOutOfBoundsException ();
689 if (blockDataCount + len < BUFFER_SIZE)
691 System.arraycopy (b, off, blockData, blockDataCount, len);
692 blockDataCount += len;
694 else
696 drain ();
697 writeBlockDataHeader (len);
698 realOutput.write (b, off, len);
701 else
702 realOutput.write (b, off, len);
707 * @see DataOutputStream#flush()
709 public void flush () throws IOException
711 drain ();
712 realOutput.flush ();
717 * Causes the block-data buffer to be written to the underlying
718 * stream, but does not flush underlying stream.
720 * @exception IOException Exception from underlying
721 * <code>OutputStream</code>.
723 protected void drain () throws IOException
725 if (blockDataCount == 0)
726 return;
728 if (writeDataAsBlocks)
729 writeBlockDataHeader (blockDataCount);
730 realOutput.write (blockData, 0, blockDataCount);
731 blockDataCount = 0;
736 * @see java.io.DataOutputStream#close ()
738 public void close () throws IOException
740 flush ();
741 realOutput.close ();
746 * @see java.io.DataOutputStream#writeBoolean (boolean)
748 public void writeBoolean (boolean data) throws IOException
750 blockDataOutput.writeBoolean (data);
755 * @see java.io.DataOutputStream#writeByte (int)
757 public void writeByte (int data) throws IOException
759 blockDataOutput.writeByte (data);
764 * @see java.io.DataOutputStream#writeShort (int)
766 public void writeShort (int data) throws IOException
768 blockDataOutput.writeShort (data);
773 * @see java.io.DataOutputStream#writeChar (int)
775 public void writeChar (int data) throws IOException
777 blockDataOutput.writeChar (data);
782 * @see java.io.DataOutputStream#writeInt (int)
784 public void writeInt (int data) throws IOException
786 blockDataOutput.writeInt (data);
791 * @see java.io.DataOutputStream#writeLong (long)
793 public void writeLong (long data) throws IOException
795 blockDataOutput.writeLong (data);
800 * @see java.io.DataOutputStream#writeFloat (float)
802 public void writeFloat (float data) throws IOException
804 blockDataOutput.writeFloat (data);
809 * @see java.io.DataOutputStream#writeDouble (double)
811 public void writeDouble (double data) throws IOException
813 blockDataOutput.writeDouble (data);
818 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
820 public void writeBytes (String data) throws IOException
822 blockDataOutput.writeBytes (data);
827 * @see java.io.DataOutputStream#writeChars (java.lang.String)
829 public void writeChars (String data) throws IOException
831 dataOutput.writeChars (data);
836 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
838 public void writeUTF (String data) throws IOException
840 dataOutput.writeUTF (data);
845 * This class allows a class to specify exactly which fields should
846 * be written, and what values should be written for these fields.
848 * XXX: finish up comments
850 public static abstract class PutField
852 public abstract void put (String name, boolean value);
853 public abstract void put (String name, byte value);
854 public abstract void put (String name, char value);
855 public abstract void put (String name, double value);
856 public abstract void put (String name, float value);
857 public abstract void put (String name, int value);
858 public abstract void put (String name, long value);
859 public abstract void put (String name, short value);
860 public abstract void put (String name, Object value);
863 * @deprecated
865 public abstract void write (ObjectOutput out) throws IOException;
868 public PutField putFields () throws IOException
870 if (currentPutField == null)
872 currentPutField = new PutField ()
874 private byte[] prim_field_data =
875 new byte[currentObjectStreamClass.primFieldSize];
876 private Object[] objs =
877 new Object[currentObjectStreamClass.objectFieldCount];
879 public void put (String name, boolean value)
881 ObjectStreamField field
882 = currentObjectStreamClass.getField (name);
883 checkType (field, 'Z');
884 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
887 public void put (String name, byte value)
889 ObjectStreamField field
890 = currentObjectStreamClass.getField (name);
891 checkType (field, 'B');
892 prim_field_data[field.getOffset ()] = value;
895 public void put (String name, char value)
897 ObjectStreamField field
898 = currentObjectStreamClass.getField (name);
899 checkType (field, 'C');
900 int off = field.getOffset ();
901 prim_field_data[off++] = (byte)(value >>> 8);
902 prim_field_data[off] = (byte)value;
905 public void put (String name, double value)
907 ObjectStreamField field
908 = currentObjectStreamClass.getField (name);
909 checkType (field, 'D');
910 int off = field.getOffset ();
911 long l_value = Double.doubleToLongBits (value);
912 prim_field_data[off++] = (byte)(l_value >>> 52);
913 prim_field_data[off++] = (byte)(l_value >>> 48);
914 prim_field_data[off++] = (byte)(l_value >>> 40);
915 prim_field_data[off++] = (byte)(l_value >>> 32);
916 prim_field_data[off++] = (byte)(l_value >>> 24);
917 prim_field_data[off++] = (byte)(l_value >>> 16);
918 prim_field_data[off++] = (byte)(l_value >>> 8);
919 prim_field_data[off] = (byte)l_value;
922 public void put (String name, float value)
924 ObjectStreamField field
925 = currentObjectStreamClass.getField (name);
926 checkType (field, 'F');
927 int off = field.getOffset ();
928 int i_value = Float.floatToIntBits (value);
929 prim_field_data[off++] = (byte)(i_value >>> 24);
930 prim_field_data[off++] = (byte)(i_value >>> 16);
931 prim_field_data[off++] = (byte)(i_value >>> 8);
932 prim_field_data[off] = (byte)i_value;
935 public void put (String name, int value)
937 ObjectStreamField field
938 = currentObjectStreamClass.getField (name);
939 checkType (field, 'I');
940 int off = field.getOffset ();
941 prim_field_data[off++] = (byte)(value >>> 24);
942 prim_field_data[off++] = (byte)(value >>> 16);
943 prim_field_data[off++] = (byte)(value >>> 8);
944 prim_field_data[off] = (byte)value;
947 public void put (String name, long value)
949 ObjectStreamField field
950 = currentObjectStreamClass.getField (name);
951 checkType (field, 'J');
952 int off = field.getOffset ();
953 prim_field_data[off++] = (byte)(value >>> 52);
954 prim_field_data[off++] = (byte)(value >>> 48);
955 prim_field_data[off++] = (byte)(value >>> 40);
956 prim_field_data[off++] = (byte)(value >>> 32);
957 prim_field_data[off++] = (byte)(value >>> 24);
958 prim_field_data[off++] = (byte)(value >>> 16);
959 prim_field_data[off++] = (byte)(value >>> 8);
960 prim_field_data[off] = (byte)value;
963 public void put (String name, short value)
965 ObjectStreamField field
966 = currentObjectStreamClass.getField (name);
967 checkType (field, 'S');
968 int off = field.getOffset ();
969 prim_field_data[off++] = (byte)(value >>> 8);
970 prim_field_data[off] = (byte)value;
973 public void put (String name, Object value)
975 ObjectStreamField field
976 = currentObjectStreamClass.getField (name);
977 if (field == null)
978 throw new IllegalArgumentException ();
979 if (value != null &&
980 ! field.getType ().isAssignableFrom (value.getClass ()))
981 throw new IllegalArgumentException ();
982 objs[field.getOffset ()] = value;
985 public void write (ObjectOutput out) throws IOException
987 // Apparently Block data is not used with PutField as per
988 // empirical evidence against JDK 1.2. Also see Mauve test
989 // java.io.ObjectInputOutput.Test.GetPutField.
990 boolean oldmode = setBlockDataMode (false);
991 out.write (prim_field_data);
992 for (int i = 0; i < objs.length; ++ i)
993 out.writeObject (objs[i]);
994 setBlockDataMode (oldmode);
997 private void checkType (ObjectStreamField field, char type)
998 throws IllegalArgumentException
1000 if (TypeSignature.getEncodingOfClass(field.getType ()).charAt(0)
1001 != type)
1002 throw new IllegalArgumentException ();
1007 return currentPutField;
1011 public void writeFields () throws IOException
1013 if (currentPutField == null)
1014 throw new NotActiveException ("writeFields can only be called after putFields has been called");
1016 // putFields may be called more than once, but not writeFields.
1017 markFieldsWritten();
1019 currentPutField.write (this);
1020 currentPutField = null;
1024 // write out the block-data buffer, picking the correct header
1025 // depending on the size of the buffer
1026 private void writeBlockDataHeader (int size) throws IOException
1028 if (size < 256)
1030 realOutput.writeByte (TC_BLOCKDATA);
1031 realOutput.write (size);
1033 else
1035 realOutput.writeByte (TC_BLOCKDATALONG);
1036 realOutput.writeInt (size);
1041 // lookup the handle for OBJ, return null if OBJ doesn't have a
1042 // handle yet
1043 private Integer findHandle (Object obj)
1045 return (Integer)OIDLookupTable.get (new ObjectIdentityWrapper (obj));
1049 // assigns the next availible handle to OBJ
1050 private int assignNewHandle (Object obj)
1052 OIDLookupTable.put (new ObjectIdentityWrapper (obj),
1053 new Integer (nextOID));
1054 return nextOID++;
1058 // resets mapping from objects to handles
1059 private void clearHandles ()
1061 nextOID = baseWireHandle;
1062 OIDLookupTable.clear ();
1066 // write out array size followed by each element of the array
1067 private void writeArraySizeAndElements (Object array, Class clazz)
1068 throws IOException
1070 int length = Array.getLength (array);
1072 if (clazz.isPrimitive ())
1074 if (clazz == Boolean.TYPE)
1076 boolean[] cast_array = (boolean[])array;
1077 realOutput.writeInt (length);
1078 for (int i=0; i < length; i++)
1079 realOutput.writeBoolean (cast_array[i]);
1080 return;
1082 if (clazz == Byte.TYPE)
1084 byte[] cast_array = (byte[])array;
1085 realOutput.writeInt (length);
1086 realOutput.write(cast_array, 0, length);
1087 return;
1089 if (clazz == Character.TYPE)
1091 char[] cast_array = (char[])array;
1092 realOutput.writeInt (length);
1093 for (int i=0; i < length; i++)
1094 realOutput.writeChar (cast_array[i]);
1095 return;
1097 if (clazz == Double.TYPE)
1099 double[] cast_array = (double[])array;
1100 realOutput.writeInt (length);
1101 for (int i=0; i < length; i++)
1102 realOutput.writeDouble (cast_array[i]);
1103 return;
1105 if (clazz == Float.TYPE)
1107 float[] cast_array = (float[])array;
1108 realOutput.writeInt (length);
1109 for (int i=0; i < length; i++)
1110 realOutput.writeFloat (cast_array[i]);
1111 return;
1113 if (clazz == Integer.TYPE)
1115 int[] cast_array = (int[])array;
1116 realOutput.writeInt (length);
1117 for (int i=0; i < length; i++)
1118 realOutput.writeInt (cast_array[i]);
1119 return;
1121 if (clazz == Long.TYPE)
1123 long[] cast_array = (long[])array;
1124 realOutput.writeInt (length);
1125 for (int i=0; i < length; i++)
1126 realOutput.writeLong (cast_array[i]);
1127 return;
1129 if (clazz == Short.TYPE)
1131 short[] cast_array = (short[])array;
1132 realOutput.writeInt (length);
1133 for (int i=0; i < length; i++)
1134 realOutput.writeShort (cast_array[i]);
1135 return;
1138 else
1140 Object[] cast_array = (Object[])array;
1141 realOutput.writeInt (length);
1142 for (int i=0; i < length; i++)
1143 writeObject (cast_array[i]);
1148 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1149 // FIELDS are already in canonical order.
1150 private void writeFields (Object obj, ObjectStreamClass osc)
1151 throws IOException
1153 ObjectStreamField[] fields = osc.fields;
1154 boolean oldmode = setBlockDataMode (false);
1155 String field_name;
1156 Class type;
1157 for (int i=0; i < fields.length; i++)
1159 field_name = fields[i].getName ();
1160 type = fields[i].getType ();
1162 if (type == Boolean.TYPE)
1163 realOutput.writeBoolean (getBooleanField (obj, osc.forClass(), field_name));
1164 else if (type == Byte.TYPE)
1165 realOutput.writeByte (getByteField (obj, osc.forClass(), field_name));
1166 else if (type == Character.TYPE)
1167 realOutput.writeChar (getCharField (obj, osc.forClass(), field_name));
1168 else if (type == Double.TYPE)
1169 realOutput.writeDouble (getDoubleField (obj, osc.forClass(), field_name));
1170 else if (type == Float.TYPE)
1171 realOutput.writeFloat (getFloatField (obj, osc.forClass(), field_name));
1172 else if (type == Integer.TYPE)
1173 realOutput.writeInt (getIntField (obj, osc.forClass(), field_name));
1174 else if (type == Long.TYPE)
1175 realOutput.writeLong (getLongField (obj, osc.forClass(), field_name));
1176 else if (type == Short.TYPE)
1177 realOutput.writeShort (getShortField (obj, osc.forClass(), field_name));
1178 else
1179 writeObject (getObjectField (obj, osc.forClass(), field_name,
1180 fields[i].getTypeString ()));
1182 setBlockDataMode (oldmode);
1186 // Toggles writing primitive data to block-data buffer.
1187 private boolean setBlockDataMode (boolean on) throws IOException
1189 if (on == writeDataAsBlocks)
1190 return on;
1192 drain();
1193 boolean oldmode = writeDataAsBlocks;
1194 writeDataAsBlocks = on;
1196 if (on)
1197 dataOutput = blockDataOutput;
1198 else
1199 dataOutput = realOutput;
1201 return oldmode;
1205 private void callWriteMethod (Object obj, ObjectStreamClass osc)
1206 throws IOException
1208 Class klass = osc.forClass();
1211 Class classArgs[] = {ObjectOutputStream.class};
1212 Method m = getMethod (klass, "writeObject", classArgs);
1213 Object args[] = {this};
1214 m.invoke (obj, args);
1216 catch (NoSuchMethodException nsme)
1218 // Nothing.
1220 catch (InvocationTargetException x)
1222 /* Rethrow if possible. */
1223 Throwable exception = x.getTargetException();
1224 if (exception instanceof RuntimeException)
1225 throw (RuntimeException) exception;
1226 if (exception instanceof IOException)
1227 throw (IOException) exception;
1229 IOException ioe
1230 = new IOException ("Exception thrown from writeObject() on " +
1231 klass + ": " + exception.getClass().getName());
1232 ioe.initCause(exception);
1233 throw ioe;
1235 catch (Exception x)
1237 IOException ioe
1238 = new IOException ("Failure invoking writeObject() on " +
1239 klass + ": " + x.getClass().getName());
1240 ioe.initCause(x);
1241 throw ioe;
1245 private boolean getBooleanField (Object obj, Class klass, String field_name)
1246 throws IOException
1250 Field f = getField (klass, field_name);
1251 boolean b = f.getBoolean (obj);
1252 return b;
1254 catch (Exception _)
1256 throw new IOException ("Unexpected Exception "+_);
1260 private byte getByteField (Object obj, Class klass, String field_name)
1261 throws IOException
1265 Field f = getField (klass, field_name);
1266 byte b = f.getByte (obj);
1267 return b;
1269 catch (Exception _)
1271 throw new IOException ("Unexpected Exception "+_);
1275 private char getCharField (Object obj, Class klass, String field_name)
1276 throws IOException
1280 Field f = getField (klass, field_name);
1281 char b = f.getChar (obj);
1282 return b;
1284 catch (Exception _)
1286 throw new IOException ("Unexpected Exception "+_);
1290 private double getDoubleField (Object obj, Class klass, String field_name)
1291 throws IOException
1295 Field f = getField (klass, field_name);
1296 double b = f.getDouble (obj);
1297 return b;
1299 catch (Exception _)
1301 throw new IOException ("Unexpected Exception "+_);
1305 private float getFloatField (Object obj, Class klass, String field_name)
1306 throws IOException
1310 Field f = getField (klass, field_name);
1311 float b = f.getFloat (obj);
1312 return b;
1314 catch (Exception _)
1316 throw new IOException ("Unexpected Exception "+_);
1320 private int getIntField (Object obj, Class klass, String field_name)
1321 throws IOException
1325 Field f = getField (klass, field_name);
1326 int b = f.getInt (obj);
1327 return b;
1329 catch (Exception _)
1331 throw new IOException ("Unexpected Exception "+_);
1335 private long getLongField (Object obj, Class klass, String field_name)
1336 throws IOException
1340 Field f = getField (klass, field_name);
1341 long b = f.getLong (obj);
1342 return b;
1344 catch (Exception _)
1346 throw new IOException ("Unexpected Exception "+_);
1350 private short getShortField (Object obj, Class klass, String field_name)
1351 throws IOException
1355 Field f = getField (klass, field_name);
1356 short b = f.getShort (obj);
1357 return b;
1359 catch (Exception _)
1361 throw new IOException ("Unexpected Exception "+_);
1365 private Object getObjectField (Object obj, Class klass, String field_name,
1366 String type_code) throws IOException
1370 Field f = getField (klass, field_name);
1371 Object o = f.get (obj);
1372 // FIXME: We should check the type_code here
1373 return o;
1375 catch (Exception _)
1377 throw new IOException ("Unexpected Exception "+_);
1381 private static Field getField (Class klass, String name)
1382 throws java.lang.NoSuchFieldException
1384 final Field f = klass.getDeclaredField(name);
1385 AccessController.doPrivileged(new PrivilegedAction()
1387 public Object run()
1389 f.setAccessible(true);
1390 return null;
1393 return f;
1396 private static Method getMethod (Class klass, String name, Class[] args)
1397 throws java.lang.NoSuchMethodException
1399 final Method m = klass.getDeclaredMethod(name, args);
1400 AccessController.doPrivileged(new PrivilegedAction()
1402 public Object run()
1404 m.setAccessible(true);
1405 return null;
1408 return m;
1411 // this value comes from 1.2 spec, but is used in 1.1 as well
1412 private final static int BUFFER_SIZE = 1024;
1414 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1416 private DataOutputStream dataOutput;
1417 private boolean writeDataAsBlocks;
1418 private DataOutputStream realOutput;
1419 private DataOutputStream blockDataOutput;
1420 private byte[] blockData;
1421 private int blockDataCount;
1422 private Object currentObject;
1423 private ObjectStreamClass currentObjectStreamClass;
1424 private PutField currentPutField;
1425 private boolean fieldsAlreadyWritten;
1426 private boolean replacementEnabled;
1427 private boolean isSerializing;
1428 private int nextOID;
1429 private Hashtable OIDLookupTable;
1430 private int protocolVersion;
1431 private boolean useSubclassMethod;
1433 static
1435 if (Configuration.INIT_LOAD_LIBRARY)
1437 System.loadLibrary ("javaio");