1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
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
.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
;
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<type></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
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);
92 * ObjectInputStream ois =
93 * new ObjectInputStream (new FileInputStream ("numbers"));
94 * Hashtable newmap = (Hashtable)ois.readObject ();
96 * System.out.println (newmap);
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
];
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;
150 String val
= System
.getProperty("gcj.dumpobjects");
151 if (val
!= null && !val
.equals(""))
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
)
183 dumpElementln ("WRITE OVERRIDE: " + obj
);
185 writeObjectOverride(obj
);
190 dumpElementln ("WRITE: " + obj
);
194 boolean was_serializing
= isSerializing
;
195 boolean old_mode
= setBlockDataMode(false);
198 isSerializing
= true;
199 boolean replaceDone
= false;
200 Object replacedObject
= null;
206 realOutput
.writeByte(TC_NULL
);
210 Integer handle
= findHandle(obj
);
213 realOutput
.writeByte(TC_REFERENCE
);
214 realOutput
.writeInt(handle
.intValue());
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
)
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
);
246 if (obj
instanceof ObjectStreamClass
)
248 writeClassDescriptor((ObjectStreamClass
) obj
);
252 Class clazz
= obj
.getClass();
253 ObjectStreamClass osc
= ObjectStreamClass
.lookupForClassObject(clazz
);
255 throw new NotSerializableException(clazz
.getName());
257 if ((replacementEnabled
|| obj
instanceof Serializable
)
260 replacedObject
= obj
;
262 if (obj
instanceof Serializable
)
266 Method m
= osc
.writeReplaceMethod
;
268 obj
= m
.invoke(obj
, new Object
[0]);
270 catch (IllegalAccessException ignore
)
273 catch (InvocationTargetException ignore
)
278 if (replacementEnabled
)
279 obj
= replaceObject(obj
);
285 if (obj
instanceof String
)
287 realOutput
.writeByte(TC_STRING
);
288 assignNewHandle(obj
);
289 realOutput
.writeUTF((String
)obj
);
293 if (clazz
.isArray ())
295 realOutput
.writeByte(TC_ARRAY
);
297 assignNewHandle(obj
);
298 writeArraySizeAndElements(obj
, clazz
.getComponentType());
302 realOutput
.writeByte(TC_OBJECT
);
306 assignNewHandle(replacedObject
);
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
);
326 if (obj
instanceof Serializable
)
328 Object prevObject
= this.currentObject
;
329 ObjectStreamClass prevObjectStreamClass
= this.currentObjectStreamClass
;
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())
342 dumpElementln ("WRITE METHOD CALLED FOR: " + obj
);
343 setBlockDataMode(true);
344 callWriteMethod(obj
, currentObjectStreamClass
);
345 setBlockDataMode(false);
346 realOutput
.writeByte(TC_ENDBLOCKDATA
);
348 dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj
);
353 dumpElementln ("WRITE FIELDS CALLED FOR: " + obj
);
354 writeFields(obj
, currentObjectStreamClass
);
358 this.currentObject
= prevObject
;
359 this.currentObjectStreamClass
= prevObjectStreamClass
;
360 currentPutField
= null;
364 throw new NotSerializableException(clazz
.getName()
369 catch (ObjectStreamException ose
)
371 // Rethrow these are fatal.
374 catch (IOException e
)
376 realOutput
.writeByte(TC_EXCEPTION
);
379 setBlockDataMode(false);
384 e
.printStackTrace(System
.out
);
388 catch (IOException ioe
)
390 StreamCorruptedException ex
=
391 new StreamCorruptedException
392 (ioe
+ " thrown while exception was being written to stream.");
395 ex
.printStackTrace(System
.out
);
405 isSerializing
= was_serializing
;
406 setBlockDataMode(old_mode
);
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 boolean oldmode
= setBlockDataMode(true);
425 annotateProxyClass(osc
.forClass());
426 setBlockDataMode(oldmode
);
427 realOutput
.writeByte(TC_ENDBLOCKDATA
);
431 realOutput
.writeByte(TC_CLASSDESC
);
432 realOutput
.writeUTF(osc
.getName());
433 realOutput
.writeLong(osc
.getSerialVersionUID());
434 assignNewHandle(osc
);
436 int flags
= osc
.getFlags();
438 if (protocolVersion
== PROTOCOL_VERSION_2
439 && osc
.isExternalizable())
440 flags
|= SC_BLOCK_DATA
;
442 realOutput
.writeByte(flags
);
444 ObjectStreamField
[] fields
= osc
.fields
;
445 realOutput
.writeShort(fields
.length
);
447 ObjectStreamField field
;
448 for (int i
= 0; i
< fields
.length
; i
++)
451 realOutput
.writeByte(field
.getTypeCode ());
452 realOutput
.writeUTF(field
.getName ());
454 if (! field
.isPrimitive())
455 writeObject(field
.getTypeString());
458 boolean oldmode
= setBlockDataMode(true);
459 annotateClass(osc
.forClass());
460 setBlockDataMode(oldmode
);
461 realOutput
.writeByte(TC_ENDBLOCKDATA
);
464 if (osc
.isSerializable() || osc
.isExternalizable())
465 writeObject(osc
.getSuper());
471 * Writes the current objects non-transient, non-static fields from
472 * the current class to the underlying output stream.
474 * This method is intended to be called from within a object's
475 * <code>private void writeObject (ObjectOutputStream)</code>
478 * @exception NotActiveException This method was called from a
479 * context other than from the current object's and current class's
480 * <code>private void writeObject (ObjectOutputStream)</code>
483 * @exception IOException Exception from underlying
484 * <code>OutputStream</code>.
486 public void defaultWriteObject()
487 throws IOException
, NotActiveException
490 writeFields(currentObject
, currentObjectStreamClass
);
494 private void markFieldsWritten() throws IOException
496 if (currentObject
== null || currentObjectStreamClass
== null)
497 throw new NotActiveException
498 ("defaultWriteObject called by non-active class and/or object");
500 if (fieldsAlreadyWritten
)
501 throw new IOException
502 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
504 fieldsAlreadyWritten
= true;
508 * Resets stream to state equivalent to the state just after it was
511 * Causes all objects previously written to the stream to be
512 * forgotten. A notification of this reset is also written to the
515 * @exception IOException Exception from underlying
516 * <code>OutputStream</code> or reset called while serialization is
519 public void reset() throws IOException
525 private void reset(boolean internal
) throws IOException
530 throw new IOException("Reset called while serialization in progress");
532 realOutput
.writeByte(TC_RESET
);
540 * Informs this <code>ObjectOutputStream</code> to write data
541 * according to the specified protocol. There are currently two
542 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
543 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
544 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
547 * A non-portable method, <code>setDefaultProtocolVersion (int
548 * version)</code> is provided to change the default protocol
551 * For an explanation of the differences between the two protocols
552 * see XXX: the Java ObjectSerialization Specification.
554 * @exception IOException if <code>version</code> is not a valid
557 * @see #setDefaultProtocolVersion(int)
559 public void useProtocolVersion(int version
) throws IOException
561 if (version
!= PROTOCOL_VERSION_1
&& version
!= PROTOCOL_VERSION_2
)
562 throw new IOException("Invalid protocol version requested.");
564 protocolVersion
= version
;
569 * <em>GNU $classpath specific</em>
571 * Changes the default stream protocol used by all
572 * <code>ObjectOutputStream</code>s. There are currently two
573 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
574 * and <code>PROTOCOL_VERSION_2</code>. The default default is
575 * <code>PROTOCOL_VERSION_1</code>.
577 * @exception IOException if <code>version</code> is not a valid
580 * @see #useProtocolVersion(int)
582 public static void setDefaultProtocolVersion(int version
)
585 if (version
!= PROTOCOL_VERSION_1
&& version
!= PROTOCOL_VERSION_2
)
586 throw new IOException("Invalid protocol version requested.");
588 defaultProtocolVersion
= version
;
593 * An empty hook that allows subclasses to write extra information
594 * about classes to the stream. This method is called the first
595 * time each class is seen, and after all of the standard
596 * information about the class has been written.
598 * @exception IOException Exception from underlying
599 * <code>OutputStream</code>.
601 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
603 protected void annotateClass(Class cl
) throws IOException
607 protected void annotateProxyClass(Class cl
) throws IOException
612 * Allows subclasses to replace objects that are written to the
613 * stream with other objects to be written in their place. This
614 * method is called the first time each object is encountered
615 * (modulo reseting of the stream).
617 * This method must be enabled before it will be called in the
618 * serialization process.
620 * @exception IOException Exception from underlying
621 * <code>OutputStream</code>.
623 * @see #enableReplaceObject(boolean)
625 protected Object
replaceObject(Object obj
) throws IOException
632 * If <code>enable</code> is <code>true</code> and this object is
633 * trusted, then <code>replaceObject (Object)</code> will be called
634 * in subsequent calls to <code>writeObject (Object)</code>.
635 * Otherwise, <code>replaceObject (Object)</code> will not be called.
637 * @exception SecurityException This class is not trusted.
639 protected boolean enableReplaceObject(boolean enable
)
640 throws SecurityException
644 SecurityManager sm
= System
.getSecurityManager();
646 sm
.checkPermission(new SerializablePermission("enableSubstitution"));
649 boolean old_val
= replacementEnabled
;
650 replacementEnabled
= enable
;
656 * Writes stream magic and stream version information to the
659 * @exception IOException Exception from underlying
660 * <code>OutputStream</code>.
662 protected void writeStreamHeader() throws IOException
664 realOutput
.writeShort(STREAM_MAGIC
);
665 realOutput
.writeShort(STREAM_VERSION
);
669 * Protected constructor that allows subclasses to override
670 * serialization. This constructor should be called by subclasses
671 * that wish to override <code>writeObject (Object)</code>. This
672 * method does a security check <i>NOTE: currently not
673 * implemented</i>, then sets a flag that informs
674 * <code>writeObject (Object)</code> to call the subclasses
675 * <code>writeObjectOverride (Object)</code> method.
677 * @see #writeObjectOverride(Object)
679 protected ObjectOutputStream() throws IOException
, SecurityException
681 SecurityManager sec_man
= System
.getSecurityManager ();
683 sec_man
.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION
);
684 useSubclassMethod
= true;
689 * This method allows subclasses to override the default
690 * serialization mechanism provided by
691 * <code>ObjectOutputStream</code>. To make this method be used for
692 * writing objects, subclasses must invoke the 0-argument
693 * constructor on this class from there constructor.
695 * @see #ObjectOutputStream()
697 * @exception NotActiveException Subclass has arranged for this
698 * method to be called, but did not implement this method.
700 protected void writeObjectOverride(Object obj
) throws NotActiveException
,
703 throw new NotActiveException
704 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
709 * @see DataOutputStream#write(int)
711 public void write (int data
) throws IOException
713 if (writeDataAsBlocks
)
715 if (blockDataCount
== BUFFER_SIZE
)
718 blockData
[ blockDataCount
++ ] = (byte)data
;
721 realOutput
.write(data
);
726 * @see DataOutputStream#write(byte[])
728 public void write(byte[] b
) throws IOException
730 write(b
, 0, b
.length
);
735 * @see DataOutputStream#write(byte[],int,int)
737 public void write(byte[] b
, int off
, int len
) throws IOException
739 if (writeDataAsBlocks
)
742 throw new IndexOutOfBoundsException();
744 if (blockDataCount
+ len
< BUFFER_SIZE
)
746 System
.arraycopy(b
, off
, blockData
, blockDataCount
, len
);
747 blockDataCount
+= len
;
752 writeBlockDataHeader(len
);
753 realOutput
.write(b
, off
, len
);
757 realOutput
.write(b
, off
, len
);
762 * @see DataOutputStream#flush()
764 public void flush () throws IOException
772 * Causes the block-data buffer to be written to the underlying
773 * stream, but does not flush underlying stream.
775 * @exception IOException Exception from underlying
776 * <code>OutputStream</code>.
778 protected void drain() throws IOException
780 if (blockDataCount
== 0)
783 if (writeDataAsBlocks
)
784 writeBlockDataHeader(blockDataCount
);
785 realOutput
.write(blockData
, 0, blockDataCount
);
791 * @see java.io.DataOutputStream#close ()
793 public void close() throws IOException
801 * @see java.io.DataOutputStream#writeBoolean (boolean)
803 public void writeBoolean(boolean data
) throws IOException
805 blockDataOutput
.writeBoolean(data
);
810 * @see java.io.DataOutputStream#writeByte (int)
812 public void writeByte(int data
) throws IOException
814 blockDataOutput
.writeByte(data
);
819 * @see java.io.DataOutputStream#writeShort (int)
821 public void writeShort (int data
) throws IOException
823 blockDataOutput
.writeShort(data
);
828 * @see java.io.DataOutputStream#writeChar (int)
830 public void writeChar(int data
) throws IOException
832 blockDataOutput
.writeChar(data
);
837 * @see java.io.DataOutputStream#writeInt (int)
839 public void writeInt(int data
) throws IOException
841 blockDataOutput
.writeInt(data
);
846 * @see java.io.DataOutputStream#writeLong (long)
848 public void writeLong(long data
) throws IOException
850 blockDataOutput
.writeLong(data
);
855 * @see java.io.DataOutputStream#writeFloat (float)
857 public void writeFloat(float data
) throws IOException
859 blockDataOutput
.writeFloat(data
);
864 * @see java.io.DataOutputStream#writeDouble (double)
866 public void writeDouble(double data
) throws IOException
868 blockDataOutput
.writeDouble(data
);
873 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
875 public void writeBytes(String data
) throws IOException
877 blockDataOutput
.writeBytes(data
);
882 * @see java.io.DataOutputStream#writeChars (java.lang.String)
884 public void writeChars(String data
) throws IOException
886 dataOutput
.writeChars(data
);
891 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
893 public void writeUTF(String data
) throws IOException
895 dataOutput
.writeUTF(data
);
900 * This class allows a class to specify exactly which fields should
901 * be written, and what values should be written for these fields.
903 * XXX: finish up comments
905 public abstract static class PutField
907 public abstract void put (String name
, boolean value
);
908 public abstract void put (String name
, byte value
);
909 public abstract void put (String name
, char value
);
910 public abstract void put (String name
, double value
);
911 public abstract void put (String name
, float value
);
912 public abstract void put (String name
, int value
);
913 public abstract void put (String name
, long value
);
914 public abstract void put (String name
, short value
);
915 public abstract void put (String name
, Object value
);
920 public abstract void write (ObjectOutput out
) throws IOException
;
923 public PutField
putFields() throws IOException
925 if (currentPutField
!= null)
926 return currentPutField
;
928 currentPutField
= new PutField()
930 private byte[] prim_field_data
931 = new byte[currentObjectStreamClass
.primFieldSize
];
932 private Object
[] objs
933 = new Object
[currentObjectStreamClass
.objectFieldCount
];
935 private ObjectStreamField
getField (String name
)
937 ObjectStreamField field
938 = currentObjectStreamClass
.getField(name
);
941 throw new IllegalArgumentException("no such serializable field " + name
);
946 public void put(String name
, boolean value
)
948 ObjectStreamField field
= getField(name
);
950 checkType(field
, 'Z');
951 prim_field_data
[field
.getOffset ()] = (byte)(value ?
1 : 0);
954 public void put(String name
, byte value
)
956 ObjectStreamField field
= getField(name
);
958 checkType(field
, 'B');
959 prim_field_data
[field
.getOffset()] = value
;
962 public void put(String name
, char value
)
964 ObjectStreamField field
= getField(name
);
966 checkType(field
, 'C');
967 int off
= field
.getOffset();
968 prim_field_data
[off
++] = (byte)(value
>>> 8);
969 prim_field_data
[off
] = (byte)value
;
972 public void put(String name
, double value
)
974 ObjectStreamField field
= getField (name
);
976 checkType(field
, 'D');
977 int off
= field
.getOffset();
978 long l_value
= Double
.doubleToLongBits (value
);
979 prim_field_data
[off
++] = (byte)(l_value
>>> 52);
980 prim_field_data
[off
++] = (byte)(l_value
>>> 48);
981 prim_field_data
[off
++] = (byte)(l_value
>>> 40);
982 prim_field_data
[off
++] = (byte)(l_value
>>> 32);
983 prim_field_data
[off
++] = (byte)(l_value
>>> 24);
984 prim_field_data
[off
++] = (byte)(l_value
>>> 16);
985 prim_field_data
[off
++] = (byte)(l_value
>>> 8);
986 prim_field_data
[off
] = (byte)l_value
;
989 public void put(String name
, float value
)
991 ObjectStreamField field
= getField(name
);
993 checkType(field
, 'F');
994 int off
= field
.getOffset();
995 int i_value
= Float
.floatToIntBits(value
);
996 prim_field_data
[off
++] = (byte)(i_value
>>> 24);
997 prim_field_data
[off
++] = (byte)(i_value
>>> 16);
998 prim_field_data
[off
++] = (byte)(i_value
>>> 8);
999 prim_field_data
[off
] = (byte)i_value
;
1002 public void put(String name
, int value
)
1004 ObjectStreamField field
= getField(name
);
1005 checkType(field
, 'I');
1006 int off
= field
.getOffset();
1007 prim_field_data
[off
++] = (byte)(value
>>> 24);
1008 prim_field_data
[off
++] = (byte)(value
>>> 16);
1009 prim_field_data
[off
++] = (byte)(value
>>> 8);
1010 prim_field_data
[off
] = (byte)value
;
1013 public void put(String name
, long value
)
1015 ObjectStreamField field
= getField(name
);
1016 checkType(field
, 'J');
1017 int off
= field
.getOffset();
1018 prim_field_data
[off
++] = (byte)(value
>>> 52);
1019 prim_field_data
[off
++] = (byte)(value
>>> 48);
1020 prim_field_data
[off
++] = (byte)(value
>>> 40);
1021 prim_field_data
[off
++] = (byte)(value
>>> 32);
1022 prim_field_data
[off
++] = (byte)(value
>>> 24);
1023 prim_field_data
[off
++] = (byte)(value
>>> 16);
1024 prim_field_data
[off
++] = (byte)(value
>>> 8);
1025 prim_field_data
[off
] = (byte)value
;
1028 public void put(String name
, short value
)
1030 ObjectStreamField field
= getField(name
);
1031 checkType(field
, 'S');
1032 int off
= field
.getOffset();
1033 prim_field_data
[off
++] = (byte)(value
>>> 8);
1034 prim_field_data
[off
] = (byte)value
;
1037 public void put(String name
, Object value
)
1039 ObjectStreamField field
= getField(name
);
1041 if (value
!= null &&
1042 ! field
.getType().isAssignableFrom(value
.getClass ()))
1043 throw new IllegalArgumentException("Class " + value
.getClass() +
1044 " cannot be cast to " + field
.getType());
1045 objs
[field
.getOffset()] = value
;
1048 public void write(ObjectOutput out
) throws IOException
1050 // Apparently Block data is not used with PutField as per
1051 // empirical evidence against JDK 1.2. Also see Mauve test
1052 // java.io.ObjectInputOutput.Test.GetPutField.
1053 boolean oldmode
= setBlockDataMode(false);
1054 out
.write(prim_field_data
);
1055 for (int i
= 0; i
< objs
.length
; ++ i
)
1056 out
.writeObject(objs
[i
]);
1057 setBlockDataMode(oldmode
);
1060 private void checkType(ObjectStreamField field
, char type
)
1061 throws IllegalArgumentException
1063 if (TypeSignature
.getEncodingOfClass(field
.getType()).charAt(0)
1065 throw new IllegalArgumentException();
1070 return currentPutField
;
1074 public void writeFields() throws IOException
1076 if (currentPutField
== null)
1077 throw new NotActiveException("writeFields can only be called after putFields has been called");
1079 markFieldsWritten();
1080 currentPutField
.write(this);
1084 // write out the block-data buffer, picking the correct header
1085 // depending on the size of the buffer
1086 private void writeBlockDataHeader(int size
) throws IOException
1090 realOutput
.writeByte(TC_BLOCKDATA
);
1091 realOutput
.write(size
);
1095 realOutput
.writeByte(TC_BLOCKDATALONG
);
1096 realOutput
.writeInt(size
);
1101 // lookup the handle for OBJ, return null if OBJ doesn't have a
1103 private Integer
findHandle(Object obj
)
1105 return (Integer
)OIDLookupTable
.get(new ObjectIdentityWrapper(obj
));
1109 // assigns the next availible handle to OBJ
1110 private int assignNewHandle(Object obj
)
1112 OIDLookupTable
.put(new ObjectIdentityWrapper(obj
),
1113 new Integer(nextOID
));
1118 // resets mapping from objects to handles
1119 private void clearHandles()
1121 nextOID
= baseWireHandle
;
1122 OIDLookupTable
.clear();
1126 // write out array size followed by each element of the array
1127 private void writeArraySizeAndElements(Object array
, Class clazz
)
1130 int length
= Array
.getLength(array
);
1132 if (clazz
.isPrimitive())
1134 if (clazz
== Boolean
.TYPE
)
1136 boolean[] cast_array
= (boolean[])array
;
1137 realOutput
.writeInt (length
);
1138 for (int i
= 0; i
< length
; i
++)
1139 realOutput
.writeBoolean(cast_array
[i
]);
1142 if (clazz
== Byte
.TYPE
)
1144 byte[] cast_array
= (byte[])array
;
1145 realOutput
.writeInt(length
);
1146 realOutput
.write(cast_array
, 0, length
);
1149 if (clazz
== Character
.TYPE
)
1151 char[] cast_array
= (char[])array
;
1152 realOutput
.writeInt(length
);
1153 for (int i
= 0; i
< length
; i
++)
1154 realOutput
.writeChar(cast_array
[i
]);
1157 if (clazz
== Double
.TYPE
)
1159 double[] cast_array
= (double[])array
;
1160 realOutput
.writeInt(length
);
1161 for (int i
= 0; i
< length
; i
++)
1162 realOutput
.writeDouble(cast_array
[i
]);
1165 if (clazz
== Float
.TYPE
)
1167 float[] cast_array
= (float[])array
;
1168 realOutput
.writeInt(length
);
1169 for (int i
= 0; i
< length
; i
++)
1170 realOutput
.writeFloat(cast_array
[i
]);
1173 if (clazz
== Integer
.TYPE
)
1175 int[] cast_array
= (int[])array
;
1176 realOutput
.writeInt(length
);
1177 for (int i
= 0; i
< length
; i
++)
1178 realOutput
.writeInt(cast_array
[i
]);
1181 if (clazz
== Long
.TYPE
)
1183 long[] cast_array
= (long[])array
;
1184 realOutput
.writeInt (length
);
1185 for (int i
= 0; i
< length
; i
++)
1186 realOutput
.writeLong(cast_array
[i
]);
1189 if (clazz
== Short
.TYPE
)
1191 short[] cast_array
= (short[])array
;
1192 realOutput
.writeInt (length
);
1193 for (int i
= 0; i
< length
; i
++)
1194 realOutput
.writeShort(cast_array
[i
]);
1200 Object
[] cast_array
= (Object
[])array
;
1201 realOutput
.writeInt(length
);
1202 for (int i
= 0; i
< length
; i
++)
1203 writeObject(cast_array
[i
]);
1208 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1209 // FIELDS are already in canonical order.
1210 private void writeFields(Object obj
, ObjectStreamClass osc
)
1213 ObjectStreamField
[] fields
= osc
.fields
;
1214 boolean oldmode
= setBlockDataMode(false);
1218 for (int i
= 0; i
< fields
.length
; i
++)
1220 field_name
= fields
[i
].getName();
1221 type
= fields
[i
].getType();
1224 dumpElementln ("WRITE FIELD: " + field_name
+ " type=" + type
);
1226 if (type
== Boolean
.TYPE
)
1227 realOutput
.writeBoolean(getBooleanField(obj
, osc
.forClass(), field_name
));
1228 else if (type
== Byte
.TYPE
)
1229 realOutput
.writeByte(getByteField(obj
, osc
.forClass(), field_name
));
1230 else if (type
== Character
.TYPE
)
1231 realOutput
.writeChar(getCharField(obj
, osc
.forClass(), field_name
));
1232 else if (type
== Double
.TYPE
)
1233 realOutput
.writeDouble(getDoubleField(obj
, osc
.forClass(), field_name
));
1234 else if (type
== Float
.TYPE
)
1235 realOutput
.writeFloat(getFloatField(obj
, osc
.forClass(), field_name
));
1236 else if (type
== Integer
.TYPE
)
1237 realOutput
.writeInt(getIntField(obj
, osc
.forClass(), field_name
));
1238 else if (type
== Long
.TYPE
)
1239 realOutput
.writeLong(getLongField(obj
, osc
.forClass(), field_name
));
1240 else if (type
== Short
.TYPE
)
1241 realOutput
.writeShort(getShortField(obj
, osc
.forClass(), field_name
));
1243 writeObject(getObjectField(obj
, osc
.forClass(), field_name
,
1244 fields
[i
].getTypeString ()));
1246 setBlockDataMode(oldmode
);
1250 // Toggles writing primitive data to block-data buffer.
1251 // Package-private to avoid a trampoline constructor.
1252 boolean setBlockDataMode(boolean on
) throws IOException
1254 if (on
== writeDataAsBlocks
)
1258 boolean oldmode
= writeDataAsBlocks
;
1259 writeDataAsBlocks
= on
;
1262 dataOutput
= blockDataOutput
;
1264 dataOutput
= realOutput
;
1270 private void callWriteMethod(Object obj
, ObjectStreamClass osc
)
1273 currentPutField
= null;
1276 Object args
[] = {this};
1277 osc
.writeObjectMethod
.invoke(obj
, args
);
1279 catch (InvocationTargetException x
)
1281 /* Rethrow if possible. */
1282 Throwable exception
= x
.getTargetException();
1283 if (exception
instanceof RuntimeException
)
1284 throw (RuntimeException
) exception
;
1285 if (exception
instanceof IOException
)
1286 throw (IOException
) exception
;
1289 = new IOException("Exception thrown from writeObject() on " +
1290 osc
.forClass().getName() + ": " +
1291 exception
.getClass().getName());
1292 ioe
.initCause(exception
);
1298 = new IOException("Failure invoking writeObject() on " +
1299 osc
.forClass().getName() + ": " +
1300 x
.getClass().getName());
1306 private boolean getBooleanField(Object obj
, Class klass
, String field_name
)
1311 Field f
= getField(klass
, field_name
);
1312 boolean b
= f
.getBoolean(obj
);
1315 catch (IllegalArgumentException _
)
1317 throw new InvalidClassException
1318 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1320 catch (IOException e
)
1326 throw new IOException("Unexpected exception " + _
);
1330 private byte getByteField (Object obj
, Class klass
, String field_name
)
1335 Field f
= getField (klass
, field_name
);
1336 byte b
= f
.getByte (obj
);
1339 catch (IllegalArgumentException _
)
1341 throw new InvalidClassException
1342 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1344 catch (IOException e
)
1350 throw new IOException("Unexpected exception " + _
);
1354 private char getCharField (Object obj
, Class klass
, String field_name
)
1359 Field f
= getField (klass
, field_name
);
1360 char b
= f
.getChar (obj
);
1363 catch (IllegalArgumentException _
)
1365 throw new InvalidClassException
1366 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1368 catch (IOException e
)
1374 throw new IOException("Unexpected exception " + _
);
1378 private double getDoubleField (Object obj
, Class klass
, String field_name
)
1383 Field f
= getField (klass
, field_name
);
1384 double b
= f
.getDouble (obj
);
1387 catch (IllegalArgumentException _
)
1389 throw new InvalidClassException
1390 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1392 catch (IOException e
)
1398 throw new IOException("Unexpected exception " + _
);
1402 private float getFloatField (Object obj
, Class klass
, String field_name
)
1407 Field f
= getField (klass
, field_name
);
1408 float b
= f
.getFloat (obj
);
1411 catch (IllegalArgumentException _
)
1413 throw new InvalidClassException
1414 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1416 catch (IOException e
)
1422 throw new IOException("Unexpected exception " + _
);
1426 private int getIntField (Object obj
, Class klass
, String field_name
)
1431 Field f
= getField (klass
, field_name
);
1432 int b
= f
.getInt (obj
);
1435 catch (IllegalArgumentException _
)
1437 throw new InvalidClassException
1438 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1440 catch (IOException e
)
1446 throw new IOException("Unexpected exception " + _
);
1450 private long getLongField (Object obj
, Class klass
, String field_name
)
1455 Field f
= getField (klass
, field_name
);
1456 long b
= f
.getLong (obj
);
1459 catch (IllegalArgumentException _
)
1461 throw new InvalidClassException
1462 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1464 catch (IOException e
)
1470 throw new IOException("Unexpected exception " + _
);
1474 private short getShortField (Object obj
, Class klass
, String field_name
)
1479 Field f
= getField (klass
, field_name
);
1480 short b
= f
.getShort (obj
);
1483 catch (IllegalArgumentException _
)
1485 throw new InvalidClassException
1486 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1488 catch (IOException e
)
1494 throw new IOException("Unexpected exception " + _
);
1498 private Object
getObjectField (Object obj
, Class klass
, String field_name
,
1499 String type_code
) throws IOException
1503 Field f
= getField (klass
, field_name
);
1504 ObjectStreamField of
= new ObjectStreamField(f
.getName(), f
.getType());
1506 /* if of is primitive something went wrong
1507 * in the check for primitive classes in writeFields.
1509 if (of
.isPrimitive())
1510 throw new InvalidClassException
1511 ("invalid type code for " + field_name
+ " in class " + klass
.getName() + " : object stream field is primitive");
1513 if (!of
.getTypeString().equals(type_code
))
1514 throw new InvalidClassException
1515 ("invalid type code for " + field_name
+ " in class " + klass
.getName() + " : object stream field " + of
+ " has type string " + of
.getTypeString() + " instead of " + type_code
);
1517 Object o
= f
.get (obj
);
1518 // FIXME: We should check the type_code here
1521 catch (IOException e
)
1527 throw new IOException ();
1531 private Field
getField (Class klass
, String name
)
1532 throws java
.io
.InvalidClassException
1536 final Field f
= klass
.getDeclaredField(name
);
1537 setAccessible
.setMember(f
);
1538 AccessController
.doPrivileged(setAccessible
);
1541 catch (java
.lang
.NoSuchFieldException e
)
1543 throw new InvalidClassException
1544 ("no field called " + name
+ " in class " + klass
.getName());
1548 private void dumpElementln (String msg
)
1550 for (int i
= 0; i
< depth
; i
++)
1551 System
.out
.print (" ");
1552 System
.out
.print (Thread
.currentThread() + ": ");
1553 System
.out
.println(msg
);
1556 // this value comes from 1.2 spec, but is used in 1.1 as well
1557 private static final int BUFFER_SIZE
= 1024;
1559 private static int defaultProtocolVersion
= PROTOCOL_VERSION_2
;
1561 private DataOutputStream dataOutput
;
1562 private boolean writeDataAsBlocks
;
1563 private DataOutputStream realOutput
;
1564 private DataOutputStream blockDataOutput
;
1565 private byte[] blockData
;
1566 private int blockDataCount
;
1567 private Object currentObject
;
1568 // Package-private to avoid a trampoline.
1569 ObjectStreamClass currentObjectStreamClass
;
1570 private PutField currentPutField
;
1571 private boolean fieldsAlreadyWritten
;
1572 private boolean replacementEnabled
;
1573 private boolean isSerializing
;
1574 private int nextOID
;
1575 private Hashtable OIDLookupTable
;
1576 private int protocolVersion
;
1577 private boolean useSubclassMethod
;
1578 private SetAccessibleAction setAccessible
= new SetAccessibleAction();
1580 // The nesting depth for debugging output
1581 private int depth
= 0;
1583 // Set if we're generating debugging dumps
1584 private boolean dump
= false;
1586 private static final boolean DEBUG
= false;