Merge from mainline.
[official-gcc.git] / libjava / classpath / java / io / ObjectOutputStream.java
blob61f07bc7cfb86b445980fadb2bc40ce5fe8bcca7
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)
10 any later version.
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
20 02110-1301 USA.
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
25 combination.
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. */
40 package java.io;
42 import gnu.java.io.ObjectIdentityWrapper;
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;
50 import java.security.AccessController;
51 import java.util.Hashtable;
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();
148 if (DEBUG)
150 String val = System.getProperty("gcj.dumpobjects");
151 if (val != null && !val.equals(""))
152 dump = true;
157 * Writes a representation of <code>obj</code> to the underlying
158 * output stream by writing out information about its class, then
159 * writing out each of the objects non-transient, non-static
160 * fields. If any of these fields are other objects,
161 * they are written out in the same manner.
163 * This method can be overriden by a class by implementing
164 * <code>private void writeObject (ObjectOutputStream)</code>.
166 * If an exception is thrown from this method, the stream is left in
167 * an undefined state.
169 * @exception NotSerializableException An attempt was made to
170 * serialize an <code>Object</code> that is not serializable.
172 * @exception InvalidClassException Somebody tried to serialize
173 * an object which is wrongly formatted.
175 * @exception IOException Exception from underlying
176 * <code>OutputStream</code>.
178 public final void writeObject(Object obj) throws IOException
180 if (useSubclassMethod)
182 if (dump)
183 dumpElementln ("WRITE OVERRIDE: " + obj);
185 writeObjectOverride(obj);
186 return;
189 if (dump)
190 dumpElementln ("WRITE: " + obj);
192 depth += 2;
194 boolean was_serializing = isSerializing;
195 boolean old_mode = setBlockDataMode(false);
198 isSerializing = true;
199 boolean replaceDone = false;
200 Object replacedObject = null;
202 while (true)
204 if (obj == null)
206 realOutput.writeByte(TC_NULL);
207 break;
210 Integer handle = findHandle(obj);
211 if (handle != null)
213 realOutput.writeByte(TC_REFERENCE);
214 realOutput.writeInt(handle.intValue());
215 break;
218 if (obj instanceof Class)
220 Class cl = (Class)obj;
221 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
222 realOutput.writeByte(TC_CLASS);
223 if (!osc.isProxyClass)
225 writeObject (osc);
227 else
229 realOutput.writeByte(TC_PROXYCLASSDESC);
230 Class[] intfs = cl.getInterfaces();
231 realOutput.writeInt(intfs.length);
232 for (int i = 0; i < intfs.length; i++)
233 realOutput.writeUTF(intfs[i].getName());
235 boolean oldmode = setBlockDataMode(true);
236 annotateProxyClass(cl);
237 setBlockDataMode(oldmode);
238 realOutput.writeByte(TC_ENDBLOCKDATA);
240 writeObject(osc.getSuper());
242 assignNewHandle(obj);
243 break;
246 if (obj instanceof ObjectStreamClass)
248 writeClassDescriptor((ObjectStreamClass) obj);
249 break;
252 Class clazz = obj.getClass();
253 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
254 if (osc == null)
255 throw new NotSerializableException(clazz.getName());
257 if ((replacementEnabled || obj instanceof Serializable)
258 && ! replaceDone)
260 replacedObject = obj;
262 if (obj instanceof Serializable)
266 Method m = osc.writeReplaceMethod;
267 if (m != null)
268 obj = m.invoke(obj, new Object[0]);
270 catch (IllegalAccessException ignore)
273 catch (InvocationTargetException ignore)
278 if (replacementEnabled)
279 obj = replaceObject(obj);
281 replaceDone = true;
282 continue;
285 if (obj instanceof String)
287 realOutput.writeByte(TC_STRING);
288 assignNewHandle(obj);
289 realOutput.writeUTF((String)obj);
290 break;
293 if (clazz.isArray ())
295 realOutput.writeByte(TC_ARRAY);
296 writeObject(osc);
297 assignNewHandle(obj);
298 writeArraySizeAndElements(obj, clazz.getComponentType());
299 break;
302 realOutput.writeByte(TC_OBJECT);
303 writeObject(osc);
305 if (replaceDone)
306 assignNewHandle(replacedObject);
307 else
308 assignNewHandle(obj);
310 if (obj instanceof Externalizable)
312 if (protocolVersion == PROTOCOL_VERSION_2)
313 setBlockDataMode(true);
315 ((Externalizable)obj).writeExternal(this);
317 if (protocolVersion == PROTOCOL_VERSION_2)
319 setBlockDataMode(false);
320 realOutput.writeByte(TC_ENDBLOCKDATA);
323 break;
326 if (obj instanceof Serializable)
328 Object prevObject = this.currentObject;
329 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
330 currentObject = obj;
331 ObjectStreamClass[] hierarchy =
332 ObjectStreamClass.getObjectStreamClasses(clazz);
334 for (int i = 0; i < hierarchy.length; i++)
336 currentObjectStreamClass = hierarchy[i];
338 fieldsAlreadyWritten = false;
339 if (currentObjectStreamClass.hasWriteMethod())
341 if (dump)
342 dumpElementln ("WRITE METHOD CALLED FOR: " + obj);
343 setBlockDataMode(true);
344 callWriteMethod(obj, currentObjectStreamClass);
345 setBlockDataMode(false);
346 realOutput.writeByte(TC_ENDBLOCKDATA);
347 if (dump)
348 dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj);
350 else
352 if (dump)
353 dumpElementln ("WRITE FIELDS CALLED FOR: " + obj);
354 writeFields(obj, currentObjectStreamClass);
358 this.currentObject = prevObject;
359 this.currentObjectStreamClass = prevObjectStreamClass;
360 currentPutField = null;
361 break;
364 throw new NotSerializableException(clazz.getName()
365 + " in "
366 + obj.getClass());
367 } // end pseudo-loop
369 catch (ObjectStreamException ose)
371 // Rethrow these are fatal.
372 throw ose;
374 catch (IOException e)
376 realOutput.writeByte(TC_EXCEPTION);
377 reset(true);
379 setBlockDataMode(false);
382 if (DEBUG)
384 e.printStackTrace(System.out);
386 writeObject(e);
388 catch (IOException ioe)
390 StreamCorruptedException ex =
391 new StreamCorruptedException
392 (ioe + " thrown while exception was being written to stream.");
393 if (DEBUG)
395 ex.printStackTrace(System.out);
397 throw ex;
400 reset (true);
403 finally
405 isSerializing = was_serializing;
406 setBlockDataMode(old_mode);
407 depth -= 2;
409 if (dump)
410 dumpElementln ("END: " + obj);
414 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
416 if (osc.isProxyClass)
418 realOutput.writeByte(TC_PROXYCLASSDESC);
419 Class[] intfs = osc.forClass().getInterfaces();
420 realOutput.writeInt(intfs.length);
421 for (int i = 0; i < intfs.length; i++)
422 realOutput.writeUTF(intfs[i].getName());
424 assignNewHandle(osc);
426 boolean oldmode = setBlockDataMode(true);
427 annotateProxyClass(osc.forClass());
428 setBlockDataMode(oldmode);
429 realOutput.writeByte(TC_ENDBLOCKDATA);
431 else
433 realOutput.writeByte(TC_CLASSDESC);
434 realOutput.writeUTF(osc.getName());
435 realOutput.writeLong(osc.getSerialVersionUID());
436 assignNewHandle(osc);
438 int flags = osc.getFlags();
440 if (protocolVersion == PROTOCOL_VERSION_2
441 && osc.isExternalizable())
442 flags |= SC_BLOCK_DATA;
444 realOutput.writeByte(flags);
446 ObjectStreamField[] fields = osc.fields;
448 if (fields == ObjectStreamClass.INVALID_FIELDS)
449 throw new InvalidClassException
450 (osc.getName(), "serialPersistentFields is invalid");
452 realOutput.writeShort(fields.length);
454 ObjectStreamField field;
455 for (int i = 0; i < fields.length; i++)
457 field = fields[i];
458 realOutput.writeByte(field.getTypeCode ());
459 realOutput.writeUTF(field.getName ());
461 if (! field.isPrimitive())
462 writeObject(field.getTypeString());
465 boolean oldmode = setBlockDataMode(true);
466 annotateClass(osc.forClass());
467 setBlockDataMode(oldmode);
468 realOutput.writeByte(TC_ENDBLOCKDATA);
471 if (osc.isSerializable() || osc.isExternalizable())
472 writeObject(osc.getSuper());
473 else
474 writeObject(null);
478 * Writes the current objects non-transient, non-static fields from
479 * the current class to the underlying output stream.
481 * This method is intended to be called from within a object's
482 * <code>private void writeObject (ObjectOutputStream)</code>
483 * method.
485 * @exception NotActiveException This method was called from a
486 * context other than from the current object's and current class's
487 * <code>private void writeObject (ObjectOutputStream)</code>
488 * method.
490 * @exception IOException Exception from underlying
491 * <code>OutputStream</code>.
493 public void defaultWriteObject()
494 throws IOException, NotActiveException
496 markFieldsWritten();
497 writeFields(currentObject, currentObjectStreamClass);
501 private void markFieldsWritten() throws IOException
503 if (currentObject == null || currentObjectStreamClass == null)
504 throw new NotActiveException
505 ("defaultWriteObject called by non-active class and/or object");
507 if (fieldsAlreadyWritten)
508 throw new IOException
509 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
511 fieldsAlreadyWritten = true;
515 * Resets stream to state equivalent to the state just after it was
516 * constructed.
518 * Causes all objects previously written to the stream to be
519 * forgotten. A notification of this reset is also written to the
520 * underlying stream.
522 * @exception IOException Exception from underlying
523 * <code>OutputStream</code> or reset called while serialization is
524 * in progress.
526 public void reset() throws IOException
528 reset(false);
532 private void reset(boolean internal) throws IOException
534 if (!internal)
536 if (isSerializing)
537 throw new IOException("Reset called while serialization in progress");
539 realOutput.writeByte(TC_RESET);
542 clearHandles();
547 * Informs this <code>ObjectOutputStream</code> to write data
548 * according to the specified protocol. There are currently two
549 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
550 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
551 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
552 * since the JDK 1.2.
553 * <p>
554 * For an explanation of the differences between the two protocols
555 * see the Java Object Serialization Specification.
556 * </p>
558 * @param version the version to use.
560 * @throws IllegalArgumentException if <code>version</code> is not a valid
561 * protocol.
562 * @throws IllegalStateException if called after the first the first object
563 * was serialized.
564 * @throws IOException if an I/O error occurs.
566 * @see ObjectStreamConstants#PROTOCOL_VERSION_1
567 * @see ObjectStreamConstants#PROTOCOL_VERSION_2
569 * @since 1.2
571 public void useProtocolVersion(int version) throws IOException
573 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
574 throw new IllegalArgumentException("Invalid protocol version requested.");
576 if (nextOID != baseWireHandle)
577 throw new IllegalStateException("Protocol version cannot be changed "
578 + "after serialization started.");
580 protocolVersion = version;
584 * An empty hook that allows subclasses to write extra information
585 * about classes to the stream. This method is called the first
586 * time each class is seen, and after all of the standard
587 * information about the class has been written.
589 * @exception IOException Exception from underlying
590 * <code>OutputStream</code>.
592 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
594 protected void annotateClass(Class cl) throws IOException
598 protected void annotateProxyClass(Class cl) throws IOException
603 * Allows subclasses to replace objects that are written to the
604 * stream with other objects to be written in their place. This
605 * method is called the first time each object is encountered
606 * (modulo reseting of the stream).
608 * This method must be enabled before it will be called in the
609 * serialization process.
611 * @exception IOException Exception from underlying
612 * <code>OutputStream</code>.
614 * @see #enableReplaceObject(boolean)
616 protected Object replaceObject(Object obj) throws IOException
618 return obj;
623 * If <code>enable</code> is <code>true</code> and this object is
624 * trusted, then <code>replaceObject (Object)</code> will be called
625 * in subsequent calls to <code>writeObject (Object)</code>.
626 * Otherwise, <code>replaceObject (Object)</code> will not be called.
628 * @exception SecurityException This class is not trusted.
630 protected boolean enableReplaceObject(boolean enable)
631 throws SecurityException
633 if (enable)
635 SecurityManager sm = System.getSecurityManager();
636 if (sm != null)
637 sm.checkPermission(new SerializablePermission("enableSubstitution"));
640 boolean old_val = replacementEnabled;
641 replacementEnabled = enable;
642 return old_val;
647 * Writes stream magic and stream version information to the
648 * underlying stream.
650 * @exception IOException Exception from underlying
651 * <code>OutputStream</code>.
653 protected void writeStreamHeader() throws IOException
655 realOutput.writeShort(STREAM_MAGIC);
656 realOutput.writeShort(STREAM_VERSION);
660 * Protected constructor that allows subclasses to override
661 * serialization. This constructor should be called by subclasses
662 * that wish to override <code>writeObject (Object)</code>. This
663 * method does a security check <i>NOTE: currently not
664 * implemented</i>, then sets a flag that informs
665 * <code>writeObject (Object)</code> to call the subclasses
666 * <code>writeObjectOverride (Object)</code> method.
668 * @see #writeObjectOverride(Object)
670 protected ObjectOutputStream() throws IOException, SecurityException
672 SecurityManager sec_man = System.getSecurityManager ();
673 if (sec_man != null)
674 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
675 useSubclassMethod = true;
680 * This method allows subclasses to override the default
681 * serialization mechanism provided by
682 * <code>ObjectOutputStream</code>. To make this method be used for
683 * writing objects, subclasses must invoke the 0-argument
684 * constructor on this class from there constructor.
686 * @see #ObjectOutputStream()
688 * @exception NotActiveException Subclass has arranged for this
689 * method to be called, but did not implement this method.
691 protected void writeObjectOverride(Object obj) throws NotActiveException,
692 IOException
694 throw new NotActiveException
695 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
700 * @see DataOutputStream#write(int)
702 public void write (int data) throws IOException
704 if (writeDataAsBlocks)
706 if (blockDataCount == BUFFER_SIZE)
707 drain();
709 blockData[ blockDataCount++ ] = (byte)data;
711 else
712 realOutput.write(data);
717 * @see DataOutputStream#write(byte[])
719 public void write(byte[] b) throws IOException
721 write(b, 0, b.length);
726 * @see DataOutputStream#write(byte[],int,int)
728 public void write(byte[] b, int off, int len) throws IOException
730 if (writeDataAsBlocks)
732 if (len < 0)
733 throw new IndexOutOfBoundsException();
735 if (blockDataCount + len < BUFFER_SIZE)
737 System.arraycopy(b, off, blockData, blockDataCount, len);
738 blockDataCount += len;
740 else
742 drain();
743 writeBlockDataHeader(len);
744 realOutput.write(b, off, len);
747 else
748 realOutput.write(b, off, len);
753 * @see DataOutputStream#flush()
755 public void flush () throws IOException
757 drain();
758 realOutput.flush();
763 * Causes the block-data buffer to be written to the underlying
764 * stream, but does not flush underlying stream.
766 * @exception IOException Exception from underlying
767 * <code>OutputStream</code>.
769 protected void drain() throws IOException
771 if (blockDataCount == 0)
772 return;
774 if (writeDataAsBlocks)
775 writeBlockDataHeader(blockDataCount);
776 realOutput.write(blockData, 0, blockDataCount);
777 blockDataCount = 0;
782 * @see java.io.DataOutputStream#close ()
784 public void close() throws IOException
786 flush();
787 realOutput.close();
792 * @see java.io.DataOutputStream#writeBoolean (boolean)
794 public void writeBoolean(boolean data) throws IOException
796 blockDataOutput.writeBoolean(data);
801 * @see java.io.DataOutputStream#writeByte (int)
803 public void writeByte(int data) throws IOException
805 blockDataOutput.writeByte(data);
810 * @see java.io.DataOutputStream#writeShort (int)
812 public void writeShort (int data) throws IOException
814 blockDataOutput.writeShort(data);
819 * @see java.io.DataOutputStream#writeChar (int)
821 public void writeChar(int data) throws IOException
823 blockDataOutput.writeChar(data);
828 * @see java.io.DataOutputStream#writeInt (int)
830 public void writeInt(int data) throws IOException
832 blockDataOutput.writeInt(data);
837 * @see java.io.DataOutputStream#writeLong (long)
839 public void writeLong(long data) throws IOException
841 blockDataOutput.writeLong(data);
846 * @see java.io.DataOutputStream#writeFloat (float)
848 public void writeFloat(float data) throws IOException
850 blockDataOutput.writeFloat(data);
855 * @see java.io.DataOutputStream#writeDouble (double)
857 public void writeDouble(double data) throws IOException
859 blockDataOutput.writeDouble(data);
864 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
866 public void writeBytes(String data) throws IOException
868 blockDataOutput.writeBytes(data);
873 * @see java.io.DataOutputStream#writeChars (java.lang.String)
875 public void writeChars(String data) throws IOException
877 dataOutput.writeChars(data);
882 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
884 public void writeUTF(String data) throws IOException
886 dataOutput.writeUTF(data);
891 * This class allows a class to specify exactly which fields should
892 * be written, and what values should be written for these fields.
894 * XXX: finish up comments
896 public abstract static class PutField
898 public abstract void put (String name, boolean value);
899 public abstract void put (String name, byte value);
900 public abstract void put (String name, char value);
901 public abstract void put (String name, double value);
902 public abstract void put (String name, float value);
903 public abstract void put (String name, int value);
904 public abstract void put (String name, long value);
905 public abstract void put (String name, short value);
906 public abstract void put (String name, Object value);
909 * @deprecated
911 public abstract void write (ObjectOutput out) throws IOException;
914 public PutField putFields() throws IOException
916 if (currentPutField != null)
917 return currentPutField;
919 currentPutField = new PutField()
921 private byte[] prim_field_data
922 = new byte[currentObjectStreamClass.primFieldSize];
923 private Object[] objs
924 = new Object[currentObjectStreamClass.objectFieldCount];
926 private ObjectStreamField getField (String name)
928 ObjectStreamField field
929 = currentObjectStreamClass.getField(name);
931 if (field == null)
932 throw new IllegalArgumentException("no such serializable field " + name);
934 return field;
937 public void put(String name, boolean value)
939 ObjectStreamField field = getField(name);
941 checkType(field, 'Z');
942 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
945 public void put(String name, byte value)
947 ObjectStreamField field = getField(name);
949 checkType(field, 'B');
950 prim_field_data[field.getOffset()] = value;
953 public void put(String name, char value)
955 ObjectStreamField field = getField(name);
957 checkType(field, 'C');
958 int off = field.getOffset();
959 prim_field_data[off++] = (byte)(value >>> 8);
960 prim_field_data[off] = (byte)value;
963 public void put(String name, double value)
965 ObjectStreamField field = getField (name);
967 checkType(field, 'D');
968 int off = field.getOffset();
969 long l_value = Double.doubleToLongBits (value);
970 prim_field_data[off++] = (byte)(l_value >>> 52);
971 prim_field_data[off++] = (byte)(l_value >>> 48);
972 prim_field_data[off++] = (byte)(l_value >>> 40);
973 prim_field_data[off++] = (byte)(l_value >>> 32);
974 prim_field_data[off++] = (byte)(l_value >>> 24);
975 prim_field_data[off++] = (byte)(l_value >>> 16);
976 prim_field_data[off++] = (byte)(l_value >>> 8);
977 prim_field_data[off] = (byte)l_value;
980 public void put(String name, float value)
982 ObjectStreamField field = getField(name);
984 checkType(field, 'F');
985 int off = field.getOffset();
986 int i_value = Float.floatToIntBits(value);
987 prim_field_data[off++] = (byte)(i_value >>> 24);
988 prim_field_data[off++] = (byte)(i_value >>> 16);
989 prim_field_data[off++] = (byte)(i_value >>> 8);
990 prim_field_data[off] = (byte)i_value;
993 public void put(String name, int value)
995 ObjectStreamField field = getField(name);
996 checkType(field, 'I');
997 int off = field.getOffset();
998 prim_field_data[off++] = (byte)(value >>> 24);
999 prim_field_data[off++] = (byte)(value >>> 16);
1000 prim_field_data[off++] = (byte)(value >>> 8);
1001 prim_field_data[off] = (byte)value;
1004 public void put(String name, long value)
1006 ObjectStreamField field = getField(name);
1007 checkType(field, 'J');
1008 int off = field.getOffset();
1009 prim_field_data[off++] = (byte)(value >>> 52);
1010 prim_field_data[off++] = (byte)(value >>> 48);
1011 prim_field_data[off++] = (byte)(value >>> 40);
1012 prim_field_data[off++] = (byte)(value >>> 32);
1013 prim_field_data[off++] = (byte)(value >>> 24);
1014 prim_field_data[off++] = (byte)(value >>> 16);
1015 prim_field_data[off++] = (byte)(value >>> 8);
1016 prim_field_data[off] = (byte)value;
1019 public void put(String name, short value)
1021 ObjectStreamField field = getField(name);
1022 checkType(field, 'S');
1023 int off = field.getOffset();
1024 prim_field_data[off++] = (byte)(value >>> 8);
1025 prim_field_data[off] = (byte)value;
1028 public void put(String name, Object value)
1030 ObjectStreamField field = getField(name);
1032 if (value != null &&
1033 ! field.getType().isAssignableFrom(value.getClass ()))
1034 throw new IllegalArgumentException("Class " + value.getClass() +
1035 " cannot be cast to " + field.getType());
1036 objs[field.getOffset()] = value;
1039 public void write(ObjectOutput out) throws IOException
1041 // Apparently Block data is not used with PutField as per
1042 // empirical evidence against JDK 1.2. Also see Mauve test
1043 // java.io.ObjectInputOutput.Test.GetPutField.
1044 boolean oldmode = setBlockDataMode(false);
1045 out.write(prim_field_data);
1046 for (int i = 0; i < objs.length; ++ i)
1047 out.writeObject(objs[i]);
1048 setBlockDataMode(oldmode);
1051 private void checkType(ObjectStreamField field, char type)
1052 throws IllegalArgumentException
1054 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1055 != type)
1056 throw new IllegalArgumentException();
1059 // end PutFieldImpl
1061 return currentPutField;
1065 public void writeFields() throws IOException
1067 if (currentPutField == null)
1068 throw new NotActiveException("writeFields can only be called after putFields has been called");
1070 markFieldsWritten();
1071 currentPutField.write(this);
1075 // write out the block-data buffer, picking the correct header
1076 // depending on the size of the buffer
1077 private void writeBlockDataHeader(int size) throws IOException
1079 if (size < 256)
1081 realOutput.writeByte(TC_BLOCKDATA);
1082 realOutput.write(size);
1084 else
1086 realOutput.writeByte(TC_BLOCKDATALONG);
1087 realOutput.writeInt(size);
1092 // lookup the handle for OBJ, return null if OBJ doesn't have a
1093 // handle yet
1094 private Integer findHandle(Object obj)
1096 return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj));
1100 // assigns the next availible handle to OBJ
1101 private int assignNewHandle(Object obj)
1103 OIDLookupTable.put(new ObjectIdentityWrapper(obj),
1104 new Integer(nextOID));
1105 return nextOID++;
1109 // resets mapping from objects to handles
1110 private void clearHandles()
1112 nextOID = baseWireHandle;
1113 OIDLookupTable.clear();
1117 // write out array size followed by each element of the array
1118 private void writeArraySizeAndElements(Object array, Class clazz)
1119 throws IOException
1121 int length = Array.getLength(array);
1123 if (clazz.isPrimitive())
1125 if (clazz == Boolean.TYPE)
1127 boolean[] cast_array = (boolean[])array;
1128 realOutput.writeInt (length);
1129 for (int i = 0; i < length; i++)
1130 realOutput.writeBoolean(cast_array[i]);
1131 return;
1133 if (clazz == Byte.TYPE)
1135 byte[] cast_array = (byte[])array;
1136 realOutput.writeInt(length);
1137 realOutput.write(cast_array, 0, length);
1138 return;
1140 if (clazz == Character.TYPE)
1142 char[] cast_array = (char[])array;
1143 realOutput.writeInt(length);
1144 for (int i = 0; i < length; i++)
1145 realOutput.writeChar(cast_array[i]);
1146 return;
1148 if (clazz == Double.TYPE)
1150 double[] cast_array = (double[])array;
1151 realOutput.writeInt(length);
1152 for (int i = 0; i < length; i++)
1153 realOutput.writeDouble(cast_array[i]);
1154 return;
1156 if (clazz == Float.TYPE)
1158 float[] cast_array = (float[])array;
1159 realOutput.writeInt(length);
1160 for (int i = 0; i < length; i++)
1161 realOutput.writeFloat(cast_array[i]);
1162 return;
1164 if (clazz == Integer.TYPE)
1166 int[] cast_array = (int[])array;
1167 realOutput.writeInt(length);
1168 for (int i = 0; i < length; i++)
1169 realOutput.writeInt(cast_array[i]);
1170 return;
1172 if (clazz == Long.TYPE)
1174 long[] cast_array = (long[])array;
1175 realOutput.writeInt (length);
1176 for (int i = 0; i < length; i++)
1177 realOutput.writeLong(cast_array[i]);
1178 return;
1180 if (clazz == Short.TYPE)
1182 short[] cast_array = (short[])array;
1183 realOutput.writeInt (length);
1184 for (int i = 0; i < length; i++)
1185 realOutput.writeShort(cast_array[i]);
1186 return;
1189 else
1191 Object[] cast_array = (Object[])array;
1192 realOutput.writeInt(length);
1193 for (int i = 0; i < length; i++)
1194 writeObject(cast_array[i]);
1199 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1200 // FIELDS are already in canonical order.
1201 private void writeFields(Object obj, ObjectStreamClass osc)
1202 throws IOException
1204 ObjectStreamField[] fields = osc.fields;
1205 boolean oldmode = setBlockDataMode(false);
1206 String field_name;
1207 Class type;
1209 for (int i = 0; i < fields.length; i++)
1211 field_name = fields[i].getName();
1212 type = fields[i].getType();
1214 if (dump)
1215 dumpElementln ("WRITE FIELD: " + field_name + " type=" + type);
1217 if (type == Boolean.TYPE)
1218 realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name));
1219 else if (type == Byte.TYPE)
1220 realOutput.writeByte(getByteField(obj, osc.forClass(), field_name));
1221 else if (type == Character.TYPE)
1222 realOutput.writeChar(getCharField(obj, osc.forClass(), field_name));
1223 else if (type == Double.TYPE)
1224 realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name));
1225 else if (type == Float.TYPE)
1226 realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name));
1227 else if (type == Integer.TYPE)
1228 realOutput.writeInt(getIntField(obj, osc.forClass(), field_name));
1229 else if (type == Long.TYPE)
1230 realOutput.writeLong(getLongField(obj, osc.forClass(), field_name));
1231 else if (type == Short.TYPE)
1232 realOutput.writeShort(getShortField(obj, osc.forClass(), field_name));
1233 else
1234 writeObject(getObjectField(obj, osc.forClass(), field_name,
1235 fields[i].getTypeString ()));
1237 setBlockDataMode(oldmode);
1241 // Toggles writing primitive data to block-data buffer.
1242 // Package-private to avoid a trampoline constructor.
1243 boolean setBlockDataMode(boolean on) throws IOException
1245 if (on == writeDataAsBlocks)
1246 return on;
1248 drain();
1249 boolean oldmode = writeDataAsBlocks;
1250 writeDataAsBlocks = on;
1252 if (on)
1253 dataOutput = blockDataOutput;
1254 else
1255 dataOutput = realOutput;
1257 return oldmode;
1261 private void callWriteMethod(Object obj, ObjectStreamClass osc)
1262 throws IOException
1264 currentPutField = null;
1267 Object args[] = {this};
1268 osc.writeObjectMethod.invoke(obj, args);
1270 catch (InvocationTargetException x)
1272 /* Rethrow if possible. */
1273 Throwable exception = x.getTargetException();
1274 if (exception instanceof RuntimeException)
1275 throw (RuntimeException) exception;
1276 if (exception instanceof IOException)
1277 throw (IOException) exception;
1279 IOException ioe
1280 = new IOException("Exception thrown from writeObject() on " +
1281 osc.forClass().getName() + ": " +
1282 exception.getClass().getName());
1283 ioe.initCause(exception);
1284 throw ioe;
1286 catch (Exception x)
1288 IOException ioe
1289 = new IOException("Failure invoking writeObject() on " +
1290 osc.forClass().getName() + ": " +
1291 x.getClass().getName());
1292 ioe.initCause(x);
1293 throw ioe;
1297 private boolean getBooleanField(Object obj, Class klass, String field_name)
1298 throws IOException
1302 Field f = getField(klass, field_name);
1303 boolean b = f.getBoolean(obj);
1304 return b;
1306 catch (IllegalArgumentException _)
1308 throw new InvalidClassException
1309 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1311 catch (IOException e)
1313 throw e;
1315 catch (Exception _)
1317 throw new IOException("Unexpected exception " + _);
1321 private byte getByteField (Object obj, Class klass, String field_name)
1322 throws IOException
1326 Field f = getField (klass, field_name);
1327 byte b = f.getByte (obj);
1328 return b;
1330 catch (IllegalArgumentException _)
1332 throw new InvalidClassException
1333 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1335 catch (IOException e)
1337 throw e;
1339 catch (Exception _)
1341 throw new IOException("Unexpected exception " + _);
1345 private char getCharField (Object obj, Class klass, String field_name)
1346 throws IOException
1350 Field f = getField (klass, field_name);
1351 char b = f.getChar (obj);
1352 return b;
1354 catch (IllegalArgumentException _)
1356 throw new InvalidClassException
1357 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1359 catch (IOException e)
1361 throw e;
1363 catch (Exception _)
1365 throw new IOException("Unexpected exception " + _);
1369 private double getDoubleField (Object obj, Class klass, String field_name)
1370 throws IOException
1374 Field f = getField (klass, field_name);
1375 double b = f.getDouble (obj);
1376 return b;
1378 catch (IllegalArgumentException _)
1380 throw new InvalidClassException
1381 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1383 catch (IOException e)
1385 throw e;
1387 catch (Exception _)
1389 throw new IOException("Unexpected exception " + _);
1393 private float getFloatField (Object obj, Class klass, String field_name)
1394 throws IOException
1398 Field f = getField (klass, field_name);
1399 float b = f.getFloat (obj);
1400 return b;
1402 catch (IllegalArgumentException _)
1404 throw new InvalidClassException
1405 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1407 catch (IOException e)
1409 throw e;
1411 catch (Exception _)
1413 throw new IOException("Unexpected exception " + _);
1417 private int getIntField (Object obj, Class klass, String field_name)
1418 throws IOException
1422 Field f = getField (klass, field_name);
1423 int b = f.getInt (obj);
1424 return b;
1426 catch (IllegalArgumentException _)
1428 throw new InvalidClassException
1429 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1431 catch (IOException e)
1433 throw e;
1435 catch (Exception _)
1437 throw new IOException("Unexpected exception " + _);
1441 private long getLongField (Object obj, Class klass, String field_name)
1442 throws IOException
1446 Field f = getField (klass, field_name);
1447 long b = f.getLong (obj);
1448 return b;
1450 catch (IllegalArgumentException _)
1452 throw new InvalidClassException
1453 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1455 catch (IOException e)
1457 throw e;
1459 catch (Exception _)
1461 throw new IOException("Unexpected exception " + _);
1465 private short getShortField (Object obj, Class klass, String field_name)
1466 throws IOException
1470 Field f = getField (klass, field_name);
1471 short b = f.getShort (obj);
1472 return b;
1474 catch (IllegalArgumentException _)
1476 throw new InvalidClassException
1477 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1479 catch (IOException e)
1481 throw e;
1483 catch (Exception _)
1485 throw new IOException("Unexpected exception " + _);
1489 private Object getObjectField (Object obj, Class klass, String field_name,
1490 String type_code) throws IOException
1494 Field f = getField (klass, field_name);
1495 ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType());
1497 /* if of is primitive something went wrong
1498 * in the check for primitive classes in writeFields.
1500 if (of.isPrimitive())
1501 throw new InvalidClassException
1502 ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive");
1504 if (!of.getTypeString().equals(type_code))
1505 throw new InvalidClassException
1506 ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code);
1508 Object o = f.get (obj);
1509 // FIXME: We should check the type_code here
1510 return o;
1512 catch (IOException e)
1514 throw e;
1516 catch (Exception e)
1518 throw new IOException ();
1522 private Field getField (Class klass, String name)
1523 throws java.io.InvalidClassException
1527 final Field f = klass.getDeclaredField(name);
1528 setAccessible.setMember(f);
1529 AccessController.doPrivileged(setAccessible);
1530 return f;
1532 catch (java.lang.NoSuchFieldException e)
1534 throw new InvalidClassException
1535 ("no field called " + name + " in class " + klass.getName());
1539 private void dumpElementln (String msg)
1541 for (int i = 0; i < depth; i++)
1542 System.out.print (" ");
1543 System.out.print (Thread.currentThread() + ": ");
1544 System.out.println(msg);
1547 // this value comes from 1.2 spec, but is used in 1.1 as well
1548 private static final int BUFFER_SIZE = 1024;
1550 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1552 private DataOutputStream dataOutput;
1553 private boolean writeDataAsBlocks;
1554 private DataOutputStream realOutput;
1555 private DataOutputStream blockDataOutput;
1556 private byte[] blockData;
1557 private int blockDataCount;
1558 private Object currentObject;
1559 // Package-private to avoid a trampoline.
1560 ObjectStreamClass currentObjectStreamClass;
1561 private PutField currentPutField;
1562 private boolean fieldsAlreadyWritten;
1563 private boolean replacementEnabled;
1564 private boolean isSerializing;
1565 private int nextOID;
1566 private Hashtable OIDLookupTable;
1567 private int protocolVersion;
1568 private boolean useSubclassMethod;
1569 private SetAccessibleAction setAccessible = new SetAccessibleAction();
1571 // The nesting depth for debugging output
1572 private int depth = 0;
1574 // Set if we're generating debugging dumps
1575 private boolean dump = false;
1577 private static final boolean DEBUG = false;