1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
42 import gnu.java.io.ObjectIdentityMap2Int;
43 import gnu.java.lang.reflect.TypeSignature;
44 import gnu.java.security.action.SetAccessibleAction;
46 import java.lang.reflect.Array;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.InvocationTargetException;
49 import java.lang.reflect.Method;
53 * An <code>ObjectOutputStream</code> can be used to write objects
54 * as well as primitive data in a platform-independent manner to an
55 * <code>OutputStream</code>.
57 * The data produced by an <code>ObjectOutputStream</code> can be read
58 * and reconstituted by an <code>ObjectInputStream</code>.
60 * <code>writeObject (Object)</code> is used to write Objects, the
61 * <code>write<type></code> methods are used to write primitive
62 * data (as in <code>DataOutputStream</code>). Strings can be written
63 * as objects or as primitive data.
65 * Not all objects can be written out using an
66 * <code>ObjectOutputStream</code>. Only those objects that are an
67 * instance of <code>java.io.Serializable</code> can be written.
69 * Using default serialization, information about the class of an
70 * object is written, all of the non-transient, non-static fields of
71 * the object are written, if any of these fields are objects, they are
72 * written out in the same manner.
74 * An object is only written out the first time it is encountered. If
75 * the object is encountered later, a reference to it is written to
76 * the underlying stream. Thus writing circular object graphs
77 * does not present a problem, nor are relationships between objects
82 * Hashtable map = new Hashtable ();
83 * map.put ("one", new Integer (1));
84 * map.put ("two", new Integer (2));
86 * ObjectOutputStream oos =
87 * new ObjectOutputStream (new FileOutputStream ("numbers"));
88 * oos.writeObject (map);
91 * ObjectInputStream ois =
92 * new ObjectInputStream (new FileInputStream ("numbers"));
93 * Hashtable newmap = (Hashtable)ois.readObject ();
95 * System.out.println (newmap);
98 * The default serialization can be overriden in two ways.
100 * By defining a method <code>private void
101 * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
102 * how information about itself is written.
103 * <code>defaultWriteObject ()</code> may be called from this method to
104 * carry out default serialization. This method is not
105 * responsible for dealing with fields of super-classes or subclasses.
107 * By implementing <code>java.io.Externalizable</code>. This gives
108 * the class complete control over the way it is written to the
109 * stream. If this approach is used the burden of writing superclass
110 * and subclass data is transfered to the class implementing
111 * <code>java.io.Externalizable</code>.
113 * @see java.io.DataOutputStream
114 * @see java.io.Externalizable
115 * @see java.io.ObjectInputStream
116 * @see java.io.Serializable
117 * @author Tom Tromey (tromey@redhat.com)
118 * @author Jeroen Frijters (jeroen@frijters.net)
119 * @author Guilhem Lavaux (guilhem@kaffe.org)
120 * @author Michael Koch (konqueror@gmx.de)
121 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
123 public class ObjectOutputStream extends OutputStream
124 implements ObjectOutput, ObjectStreamConstants
127 * Creates a new <code>ObjectOutputStream</code> that will do all of
128 * its writing onto <code>out</code>. This method also initializes
129 * the stream by writing the header information (stream magic number
130 * and stream version).
132 * @exception IOException Writing stream header to underlying
133 * stream cannot be completed.
135 * @see #writeStreamHeader()
137 public ObjectOutputStream (OutputStream out) throws IOException
139 realOutput = new DataOutputStream(out);
140 blockData = new byte[ BUFFER_SIZE ];
142 blockDataOutput = new DataOutputStream(this);
143 setBlockDataMode(true);
144 replacementEnabled = false;
145 isSerializing = false;
146 nextOID = baseWireHandle;
147 OIDLookupTable = new ObjectIdentityMap2Int();
148 protocolVersion = defaultProtocolVersion;
149 useSubclassMethod = false;
154 String val = System.getProperty("gcj.dumpobjects");
155 if (val != null && !val.equals(""))
161 * Writes a representation of <code>obj</code> to the underlying
162 * output stream by writing out information about its class, then
163 * writing out each of the objects non-transient, non-static
164 * fields. If any of these fields are other objects,
165 * they are written out in the same manner.
167 * This method can be overriden by a class by implementing
168 * <code>private void writeObject (ObjectOutputStream)</code>.
170 * If an exception is thrown from this method, the stream is left in
171 * an undefined state.
173 * @exception NotSerializableException An attempt was made to
174 * serialize an <code>Object</code> that is not serializable.
176 * @exception InvalidClassException Somebody tried to serialize
177 * an object which is wrongly formatted.
179 * @exception IOException Exception from underlying
180 * <code>OutputStream</code>.
182 public final void writeObject(Object obj) throws IOException
184 if (useSubclassMethod)
187 dumpElementln ("WRITE OVERRIDE: " + obj);
189 writeObjectOverride(obj);
194 dumpElementln ("WRITE: ", obj);
198 boolean was_serializing = isSerializing;
199 boolean old_mode = setBlockDataMode(false);
202 isSerializing = true;
203 boolean replaceDone = false;
204 Object replacedObject = null;
210 realOutput.writeByte(TC_NULL);
214 int handle = findHandle(obj);
217 realOutput.writeByte(TC_REFERENCE);
218 realOutput.writeInt(handle);
222 if (obj instanceof Class)
224 Class cl = (Class)obj;
225 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
226 realOutput.writeByte(TC_CLASS);
227 if (!osc.isProxyClass)
232 {System.err.println("1");
233 realOutput.writeByte(TC_PROXYCLASSDESC);
234 Class[] intfs = cl.getInterfaces();
235 realOutput.writeInt(intfs.length);
236 for (int i = 0; i < intfs.length; i++)
237 realOutput.writeUTF(intfs[i].getName());
239 boolean oldmode = setBlockDataMode(true);
240 annotateProxyClass(cl);
241 setBlockDataMode(oldmode);
242 realOutput.writeByte(TC_ENDBLOCKDATA);
244 writeObject(osc.getSuper());
246 assignNewHandle(obj);
250 if (obj instanceof ObjectStreamClass)
252 writeClassDescriptor((ObjectStreamClass) obj);
256 Class clazz = obj.getClass();
257 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
259 throw new NotSerializableException(clazz.getName());
263 /* TC_ENUM classDesc newHandle enumConstantName */
264 realOutput.writeByte(TC_ENUM);
266 assignNewHandle(obj);
267 writeObject(((Enum) obj).name());
271 if ((replacementEnabled || obj instanceof Serializable)
274 replacedObject = obj;
276 if (obj instanceof Serializable)
280 Method m = osc.writeReplaceMethod;
282 obj = m.invoke(obj, new Object[0]);
284 catch (IllegalAccessException ignore)
287 catch (InvocationTargetException ignore)
292 if (replacementEnabled)
293 obj = replaceObject(obj);
299 if (obj instanceof String)
301 realOutput.writeByte(TC_STRING);
302 assignNewHandle(obj);
303 realOutput.writeUTF((String)obj);
307 if (clazz.isArray ())
309 realOutput.writeByte(TC_ARRAY);
311 assignNewHandle(obj);
312 writeArraySizeAndElements(obj, clazz.getComponentType());
316 realOutput.writeByte(TC_OBJECT);
320 assignNewHandle(replacedObject);
322 assignNewHandle(obj);
324 if (obj instanceof Externalizable)
326 if (protocolVersion == PROTOCOL_VERSION_2)
327 setBlockDataMode(true);
329 ((Externalizable)obj).writeExternal(this);
331 if (protocolVersion == PROTOCOL_VERSION_2)
333 setBlockDataMode(false);
334 realOutput.writeByte(TC_ENDBLOCKDATA);
340 if (obj instanceof Serializable)
342 Object prevObject = this.currentObject;
343 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
345 ObjectStreamClass[] hierarchy = osc.hierarchy();
347 for (int i = 0; i < hierarchy.length; i++)
349 currentObjectStreamClass = hierarchy[i];
351 fieldsAlreadyWritten = false;
352 if (currentObjectStreamClass.hasWriteMethod())
355 dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
356 setBlockDataMode(true);
357 callWriteMethod(obj, currentObjectStreamClass);
358 setBlockDataMode(false);
359 realOutput.writeByte(TC_ENDBLOCKDATA);
361 dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
366 dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
367 writeFields(obj, currentObjectStreamClass);
371 this.currentObject = prevObject;
372 this.currentObjectStreamClass = prevObjectStreamClass;
373 currentPutField = null;
377 throw new NotSerializableException(clazz.getName()
382 catch (ObjectStreamException ose)
384 // Rethrow these are fatal.
387 catch (IOException e)
389 realOutput.writeByte(TC_EXCEPTION);
392 setBlockDataMode(false);
397 e.printStackTrace(System.out);
401 catch (IOException ioe)
403 StreamCorruptedException ex =
404 new StreamCorruptedException
405 (ioe + " thrown while exception was being written to stream.");
408 ex.printStackTrace(System.out);
418 isSerializing = was_serializing;
419 setBlockDataMode(old_mode);
423 dumpElementln ("END: ", obj);
427 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
429 if (osc.isProxyClass)
431 realOutput.writeByte(TC_PROXYCLASSDESC);
432 Class[] intfs = osc.forClass().getInterfaces();
433 realOutput.writeInt(intfs.length);
434 for (int i = 0; i < intfs.length; i++)
435 realOutput.writeUTF(intfs[i].getName());
437 assignNewHandle(osc);
439 boolean oldmode = setBlockDataMode(true);
440 annotateProxyClass(osc.forClass());
441 setBlockDataMode(oldmode);
442 realOutput.writeByte(TC_ENDBLOCKDATA);
446 realOutput.writeByte(TC_CLASSDESC);
447 realOutput.writeUTF(osc.getName());
449 realOutput.writeLong(0L);
451 realOutput.writeLong(osc.getSerialVersionUID());
452 assignNewHandle(osc);
454 int flags = osc.getFlags();
456 if (protocolVersion == PROTOCOL_VERSION_2
457 && osc.isExternalizable())
458 flags |= SC_BLOCK_DATA;
460 realOutput.writeByte(flags);
462 ObjectStreamField[] fields = osc.fields;
464 if (fields == ObjectStreamClass.INVALID_FIELDS)
465 throw new InvalidClassException
466 (osc.getName(), "serialPersistentFields is invalid");
468 realOutput.writeShort(fields.length);
470 ObjectStreamField field;
471 for (int i = 0; i < fields.length; i++)
474 realOutput.writeByte(field.getTypeCode ());
475 realOutput.writeUTF(field.getName ());
477 if (! field.isPrimitive())
478 writeObject(field.getTypeString());
481 boolean oldmode = setBlockDataMode(true);
482 annotateClass(osc.forClass());
483 setBlockDataMode(oldmode);
484 realOutput.writeByte(TC_ENDBLOCKDATA);
487 if (osc.isSerializable() || osc.isExternalizable())
488 writeObject(osc.getSuper());
494 * Writes the current objects non-transient, non-static fields from
495 * the current class to the underlying output stream.
497 * This method is intended to be called from within a object's
498 * <code>private void writeObject (ObjectOutputStream)</code>
501 * @exception NotActiveException This method was called from a
502 * context other than from the current object's and current class's
503 * <code>private void writeObject (ObjectOutputStream)</code>
506 * @exception IOException Exception from underlying
507 * <code>OutputStream</code>.
509 public void defaultWriteObject()
510 throws IOException, NotActiveException
513 writeFields(currentObject, currentObjectStreamClass);
517 private void markFieldsWritten() throws IOException
519 if (currentObject == null || currentObjectStreamClass == null)
520 throw new NotActiveException
521 ("defaultWriteObject called by non-active class and/or object");
523 if (fieldsAlreadyWritten)
524 throw new IOException
525 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
527 fieldsAlreadyWritten = true;
531 * Resets stream to state equivalent to the state just after it was
534 * Causes all objects previously written to the stream to be
535 * forgotten. A notification of this reset is also written to the
538 * @exception IOException Exception from underlying
539 * <code>OutputStream</code> or reset called while serialization is
542 public void reset() throws IOException
548 private void reset(boolean internal) throws IOException
553 throw new IOException("Reset called while serialization in progress");
555 realOutput.writeByte(TC_RESET);
563 * Informs this <code>ObjectOutputStream</code> to write data
564 * according to the specified protocol. There are currently two
565 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
566 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
567 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
570 * For an explanation of the differences between the two protocols
571 * see the Java Object Serialization Specification.
574 * @param version the version to use.
576 * @throws IllegalArgumentException if <code>version</code> is not a valid
578 * @throws IllegalStateException if called after the first the first object
580 * @throws IOException if an I/O error occurs.
582 * @see ObjectStreamConstants#PROTOCOL_VERSION_1
583 * @see ObjectStreamConstants#PROTOCOL_VERSION_2
587 public void useProtocolVersion(int version) throws IOException
589 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
590 throw new IllegalArgumentException("Invalid protocol version requested.");
592 if (nextOID != baseWireHandle)
593 throw new IllegalStateException("Protocol version cannot be changed "
594 + "after serialization started.");
596 protocolVersion = version;
600 * An empty hook that allows subclasses to write extra information
601 * about classes to the stream. This method is called the first
602 * time each class is seen, and after all of the standard
603 * information about the class has been written.
605 * @exception IOException Exception from underlying
606 * <code>OutputStream</code>.
608 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
610 protected void annotateClass(Class<?> cl) throws IOException
614 protected void annotateProxyClass(Class<?> cl) throws IOException
619 * Allows subclasses to replace objects that are written to the
620 * stream with other objects to be written in their place. This
621 * method is called the first time each object is encountered
622 * (modulo reseting of the stream).
624 * This method must be enabled before it will be called in the
625 * serialization process.
627 * @exception IOException Exception from underlying
628 * <code>OutputStream</code>.
630 * @see #enableReplaceObject(boolean)
632 protected Object replaceObject(Object obj) throws IOException
639 * If <code>enable</code> is <code>true</code> and this object is
640 * trusted, then <code>replaceObject (Object)</code> will be called
641 * in subsequent calls to <code>writeObject (Object)</code>.
642 * Otherwise, <code>replaceObject (Object)</code> will not be called.
644 * @exception SecurityException This class is not trusted.
646 protected boolean enableReplaceObject(boolean enable)
647 throws SecurityException
651 SecurityManager sm = System.getSecurityManager();
653 sm.checkPermission(new SerializablePermission("enableSubstitution"));
656 boolean old_val = replacementEnabled;
657 replacementEnabled = enable;
663 * Writes stream magic and stream version information to the
666 * @exception IOException Exception from underlying
667 * <code>OutputStream</code>.
669 protected void writeStreamHeader() throws IOException
671 realOutput.writeShort(STREAM_MAGIC);
672 realOutput.writeShort(STREAM_VERSION);
676 * Protected constructor that allows subclasses to override
677 * serialization. This constructor should be called by subclasses
678 * that wish to override <code>writeObject (Object)</code>. This
679 * method does a security check <i>NOTE: currently not
680 * implemented</i>, then sets a flag that informs
681 * <code>writeObject (Object)</code> to call the subclasses
682 * <code>writeObjectOverride (Object)</code> method.
684 * @see #writeObjectOverride(Object)
686 protected ObjectOutputStream() throws IOException, SecurityException
688 SecurityManager sec_man = System.getSecurityManager ();
690 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
691 useSubclassMethod = true;
696 * This method allows subclasses to override the default
697 * serialization mechanism provided by
698 * <code>ObjectOutputStream</code>. To make this method be used for
699 * writing objects, subclasses must invoke the 0-argument
700 * constructor on this class from there constructor.
702 * @see #ObjectOutputStream()
704 * @exception NotActiveException Subclass has arranged for this
705 * method to be called, but did not implement this method.
707 protected void writeObjectOverride(Object obj) throws NotActiveException,
710 throw new NotActiveException
711 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
716 * @see DataOutputStream#write(int)
718 public void write (int data) throws IOException
720 if (writeDataAsBlocks)
722 if (blockDataCount == BUFFER_SIZE)
725 blockData[ blockDataCount++ ] = (byte)data;
728 realOutput.write(data);
733 * @see DataOutputStream#write(byte[])
735 public void write(byte[] b) throws IOException
737 write(b, 0, b.length);
742 * @see DataOutputStream#write(byte[],int,int)
744 public void write(byte[] b, int off, int len) throws IOException
746 if (writeDataAsBlocks)
749 throw new IndexOutOfBoundsException();
751 if (blockDataCount + len < BUFFER_SIZE)
753 System.arraycopy(b, off, blockData, blockDataCount, len);
754 blockDataCount += len;
759 writeBlockDataHeader(len);
760 realOutput.write(b, off, len);
764 realOutput.write(b, off, len);
769 * @see DataOutputStream#flush()
771 public void flush () throws IOException
779 * Causes the block-data buffer to be written to the underlying
780 * stream, but does not flush underlying stream.
782 * @exception IOException Exception from underlying
783 * <code>OutputStream</code>.
785 protected void drain() throws IOException
787 if (blockDataCount == 0)
790 if (writeDataAsBlocks)
791 writeBlockDataHeader(blockDataCount);
792 realOutput.write(blockData, 0, blockDataCount);
798 * @see java.io.DataOutputStream#close ()
800 public void close() throws IOException
808 * @see java.io.DataOutputStream#writeBoolean (boolean)
810 public void writeBoolean(boolean data) throws IOException
812 blockDataOutput.writeBoolean(data);
817 * @see java.io.DataOutputStream#writeByte (int)
819 public void writeByte(int data) throws IOException
821 blockDataOutput.writeByte(data);
826 * @see java.io.DataOutputStream#writeShort (int)
828 public void writeShort (int data) throws IOException
830 blockDataOutput.writeShort(data);
835 * @see java.io.DataOutputStream#writeChar (int)
837 public void writeChar(int data) throws IOException
839 blockDataOutput.writeChar(data);
844 * @see java.io.DataOutputStream#writeInt (int)
846 public void writeInt(int data) throws IOException
848 blockDataOutput.writeInt(data);
853 * @see java.io.DataOutputStream#writeLong (long)
855 public void writeLong(long data) throws IOException
857 blockDataOutput.writeLong(data);
862 * @see java.io.DataOutputStream#writeFloat (float)
864 public void writeFloat(float data) throws IOException
866 blockDataOutput.writeFloat(data);
871 * @see java.io.DataOutputStream#writeDouble (double)
873 public void writeDouble(double data) throws IOException
875 blockDataOutput.writeDouble(data);
880 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
882 public void writeBytes(String data) throws IOException
884 blockDataOutput.writeBytes(data);
889 * @see java.io.DataOutputStream#writeChars (java.lang.String)
891 public void writeChars(String data) throws IOException
893 dataOutput.writeChars(data);
898 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
900 public void writeUTF(String data) throws IOException
902 dataOutput.writeUTF(data);
907 * This class allows a class to specify exactly which fields should
908 * be written, and what values should be written for these fields.
910 * XXX: finish up comments
912 public abstract static class PutField
914 public abstract void put (String name, boolean value);
915 public abstract void put (String name, byte value);
916 public abstract void put (String name, char value);
917 public abstract void put (String name, double value);
918 public abstract void put (String name, float value);
919 public abstract void put (String name, int value);
920 public abstract void put (String name, long value);
921 public abstract void put (String name, short value);
922 public abstract void put (String name, Object value);
927 public abstract void write (ObjectOutput out) throws IOException;
930 public PutField putFields() throws IOException
932 if (currentPutField != null)
933 return currentPutField;
935 currentPutField = new PutField()
937 private byte[] prim_field_data
938 = new byte[currentObjectStreamClass.primFieldSize];
939 private Object[] objs
940 = new Object[currentObjectStreamClass.objectFieldCount];
942 private ObjectStreamField getField (String name)
944 ObjectStreamField field
945 = currentObjectStreamClass.getField(name);
948 throw new IllegalArgumentException("no such serializable field " + name);
953 public void put(String name, boolean value)
955 ObjectStreamField field = getField(name);
957 checkType(field, 'Z');
958 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
961 public void put(String name, byte value)
963 ObjectStreamField field = getField(name);
965 checkType(field, 'B');
966 prim_field_data[field.getOffset()] = value;
969 public void put(String name, char value)
971 ObjectStreamField field = getField(name);
973 checkType(field, 'C');
974 int off = field.getOffset();
975 prim_field_data[off++] = (byte)(value >>> 8);
976 prim_field_data[off] = (byte)value;
979 public void put(String name, double value)
981 ObjectStreamField field = getField (name);
983 checkType(field, 'D');
984 int off = field.getOffset();
985 long l_value = Double.doubleToLongBits (value);
986 prim_field_data[off++] = (byte)(l_value >>> 52);
987 prim_field_data[off++] = (byte)(l_value >>> 48);
988 prim_field_data[off++] = (byte)(l_value >>> 40);
989 prim_field_data[off++] = (byte)(l_value >>> 32);
990 prim_field_data[off++] = (byte)(l_value >>> 24);
991 prim_field_data[off++] = (byte)(l_value >>> 16);
992 prim_field_data[off++] = (byte)(l_value >>> 8);
993 prim_field_data[off] = (byte)l_value;
996 public void put(String name, float value)
998 ObjectStreamField field = getField(name);
1000 checkType(field, 'F');
1001 int off = field.getOffset();
1002 int i_value = Float.floatToIntBits(value);
1003 prim_field_data[off++] = (byte)(i_value >>> 24);
1004 prim_field_data[off++] = (byte)(i_value >>> 16);
1005 prim_field_data[off++] = (byte)(i_value >>> 8);
1006 prim_field_data[off] = (byte)i_value;
1009 public void put(String name, int value)
1011 ObjectStreamField field = getField(name);
1012 checkType(field, 'I');
1013 int off = field.getOffset();
1014 prim_field_data[off++] = (byte)(value >>> 24);
1015 prim_field_data[off++] = (byte)(value >>> 16);
1016 prim_field_data[off++] = (byte)(value >>> 8);
1017 prim_field_data[off] = (byte)value;
1020 public void put(String name, long value)
1022 ObjectStreamField field = getField(name);
1023 checkType(field, 'J');
1024 int off = field.getOffset();
1025 prim_field_data[off++] = (byte)(value >>> 52);
1026 prim_field_data[off++] = (byte)(value >>> 48);
1027 prim_field_data[off++] = (byte)(value >>> 40);
1028 prim_field_data[off++] = (byte)(value >>> 32);
1029 prim_field_data[off++] = (byte)(value >>> 24);
1030 prim_field_data[off++] = (byte)(value >>> 16);
1031 prim_field_data[off++] = (byte)(value >>> 8);
1032 prim_field_data[off] = (byte)value;
1035 public void put(String name, short value)
1037 ObjectStreamField field = getField(name);
1038 checkType(field, 'S');
1039 int off = field.getOffset();
1040 prim_field_data[off++] = (byte)(value >>> 8);
1041 prim_field_data[off] = (byte)value;
1044 public void put(String name, Object value)
1046 ObjectStreamField field = getField(name);
1048 if (value != null &&
1049 ! field.getType().isAssignableFrom(value.getClass ()))
1050 throw new IllegalArgumentException("Class " + value.getClass() +
1051 " cannot be cast to " + field.getType());
1052 objs[field.getOffset()] = value;
1055 public void write(ObjectOutput out) throws IOException
1057 // Apparently Block data is not used with PutField as per
1058 // empirical evidence against JDK 1.2. Also see Mauve test
1059 // java.io.ObjectInputOutput.Test.GetPutField.
1060 boolean oldmode = setBlockDataMode(false);
1061 out.write(prim_field_data);
1062 for (int i = 0; i < objs.length; ++ i)
1063 out.writeObject(objs[i]);
1064 setBlockDataMode(oldmode);
1067 private void checkType(ObjectStreamField field, char type)
1068 throws IllegalArgumentException
1070 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1072 throw new IllegalArgumentException();
1077 return currentPutField;
1081 public void writeFields() throws IOException
1083 if (currentPutField == null)
1084 throw new NotActiveException("writeFields can only be called after putFields has been called");
1086 markFieldsWritten();
1087 currentPutField.write(this);
1091 // write out the block-data buffer, picking the correct header
1092 // depending on the size of the buffer
1093 private void writeBlockDataHeader(int size) throws IOException
1097 realOutput.writeByte(TC_BLOCKDATA);
1098 realOutput.write(size);
1102 realOutput.writeByte(TC_BLOCKDATALONG);
1103 realOutput.writeInt(size);
1108 // lookup the handle for OBJ, return null if OBJ doesn't have a
1110 private int findHandle(Object obj)
1112 return OIDLookupTable.get(obj);
1116 // assigns the next availible handle to OBJ
1117 private int assignNewHandle(Object obj)
1119 OIDLookupTable.put(obj, nextOID);
1124 // resets mapping from objects to handles
1125 private void clearHandles()
1127 nextOID = baseWireHandle;
1128 OIDLookupTable.clear();
1132 // write out array size followed by each element of the array
1133 private void writeArraySizeAndElements(Object array, Class clazz)
1136 int length = Array.getLength(array);
1138 if (clazz.isPrimitive())
1140 if (clazz == Boolean.TYPE)
1142 boolean[] cast_array = (boolean[])array;
1143 realOutput.writeInt (length);
1144 for (int i = 0; i < length; i++)
1145 realOutput.writeBoolean(cast_array[i]);
1148 if (clazz == Byte.TYPE)
1150 byte[] cast_array = (byte[])array;
1151 realOutput.writeInt(length);
1152 realOutput.write(cast_array, 0, length);
1155 if (clazz == Character.TYPE)
1157 char[] cast_array = (char[])array;
1158 realOutput.writeInt(length);
1159 for (int i = 0; i < length; i++)
1160 realOutput.writeChar(cast_array[i]);
1163 if (clazz == Double.TYPE)
1165 double[] cast_array = (double[])array;
1166 realOutput.writeInt(length);
1167 for (int i = 0; i < length; i++)
1168 realOutput.writeDouble(cast_array[i]);
1171 if (clazz == Float.TYPE)
1173 float[] cast_array = (float[])array;
1174 realOutput.writeInt(length);
1175 for (int i = 0; i < length; i++)
1176 realOutput.writeFloat(cast_array[i]);
1179 if (clazz == Integer.TYPE)
1181 int[] cast_array = (int[])array;
1182 realOutput.writeInt(length);
1183 for (int i = 0; i < length; i++)
1184 realOutput.writeInt(cast_array[i]);
1187 if (clazz == Long.TYPE)
1189 long[] cast_array = (long[])array;
1190 realOutput.writeInt (length);
1191 for (int i = 0; i < length; i++)
1192 realOutput.writeLong(cast_array[i]);
1195 if (clazz == Short.TYPE)
1197 short[] cast_array = (short[])array;
1198 realOutput.writeInt (length);
1199 for (int i = 0; i < length; i++)
1200 realOutput.writeShort(cast_array[i]);
1206 Object[] cast_array = (Object[])array;
1207 realOutput.writeInt(length);
1208 for (int i = 0; i < length; i++)
1209 writeObject(cast_array[i]);
1215 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1216 // FIELDS are already supposed already to be in canonical order, but
1217 // under some circumstances (to do with Proxies) this isn't the
1218 // case, so we call ensureFieldsSet().
1219 private void writeFields(Object obj, ObjectStreamClass osc)
1222 osc.ensureFieldsSet(osc.forClass());
1225 ObjectStreamField[] fields = osc.fields;
1226 boolean oldmode = setBlockDataMode(false);
1230 writeFields(obj,fields);
1232 catch (IllegalArgumentException _)
1234 InvalidClassException e = new InvalidClassException
1235 ("writing fields of class " + osc.forClass().getName());
1239 catch (IOException e)
1245 IOException e = new IOException("Unexpected exception " + _);
1250 setBlockDataMode(oldmode);
1255 * Helper function for writeFields(Object,ObjectStreamClass): write
1256 * fields from given fields array. Pass exception on.
1258 * @param obj the object to be written
1260 * @param fields the fields of obj to be written.
1262 private void writeFields(Object obj, ObjectStreamField[] fields)
1264 IllegalArgumentException, IllegalAccessException, IOException
1266 for (int i = 0; i < fields.length; i++)
1268 ObjectStreamField osf = fields[i];
1269 Field field = osf.field;
1272 dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
1274 switch (osf.getTypeCode())
1276 case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
1277 case 'B': realOutput.writeByte (field.getByte (obj)); break;
1278 case 'S': realOutput.writeShort (field.getShort (obj)); break;
1279 case 'C': realOutput.writeChar (field.getChar (obj)); break;
1280 case 'I': realOutput.writeInt (field.getInt (obj)); break;
1281 case 'F': realOutput.writeFloat (field.getFloat (obj)); break;
1282 case 'J': realOutput.writeLong (field.getLong (obj)); break;
1283 case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
1285 case '[': writeObject (field.get (obj)); break;
1287 throw new IOException("Unexpected type code " + osf.getTypeCode());
1293 // Toggles writing primitive data to block-data buffer.
1294 // Package-private to avoid a trampoline constructor.
1295 boolean setBlockDataMode(boolean on) throws IOException
1297 if (on == writeDataAsBlocks)
1301 boolean oldmode = writeDataAsBlocks;
1302 writeDataAsBlocks = on;
1305 dataOutput = blockDataOutput;
1307 dataOutput = realOutput;
1313 private void callWriteMethod(Object obj, ObjectStreamClass osc)
1316 currentPutField = null;
1319 Object args[] = {this};
1320 osc.writeObjectMethod.invoke(obj, args);
1322 catch (InvocationTargetException x)
1324 /* Rethrow if possible. */
1325 Throwable exception = x.getTargetException();
1326 if (exception instanceof RuntimeException)
1327 throw (RuntimeException) exception;
1328 if (exception instanceof IOException)
1329 throw (IOException) exception;
1332 = new IOException("Exception thrown from writeObject() on " +
1333 osc.forClass().getName() + ": " +
1334 exception.getClass().getName());
1335 ioe.initCause(exception);
1341 = new IOException("Failure invoking writeObject() on " +
1342 osc.forClass().getName() + ": " +
1343 x.getClass().getName());
1349 private void dumpElementln (String msg, Object obj)
1353 for (int i = 0; i < depth; i++)
1354 System.out.print (" ");
1355 System.out.print (Thread.currentThread() + ": ");
1356 System.out.print (msg);
1357 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
1358 System.out.print (obj.getClass());
1360 System.out.print (obj);
1367 System.out.println ();
1371 private void dumpElementln (String msg)
1373 for (int i = 0; i < depth; i++)
1374 System.out.print (" ");
1375 System.out.print (Thread.currentThread() + ": ");
1376 System.out.println(msg);
1379 // this value comes from 1.2 spec, but is used in 1.1 as well
1380 private static final int BUFFER_SIZE = 1024;
1382 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1384 private DataOutputStream dataOutput;
1385 private boolean writeDataAsBlocks;
1386 private DataOutputStream realOutput;
1387 private DataOutputStream blockDataOutput;
1388 private byte[] blockData;
1389 private int blockDataCount;
1390 private Object currentObject;
1391 // Package-private to avoid a trampoline.
1392 ObjectStreamClass currentObjectStreamClass;
1393 private PutField currentPutField;
1394 private boolean fieldsAlreadyWritten;
1395 private boolean replacementEnabled;
1396 private boolean isSerializing;
1397 private int nextOID;
1398 private ObjectIdentityMap2Int OIDLookupTable;
1399 private int protocolVersion;
1400 private boolean useSubclassMethod;
1401 private SetAccessibleAction setAccessible = new SetAccessibleAction();
1403 // The nesting depth for debugging output
1404 private int depth = 0;
1406 // Set if we're generating debugging dumps
1407 private boolean dump = false;
1409 private static final boolean DEBUG = false;