Merge from the pain train
[official-gcc.git] / libjava / java / io / ObjectOutputStream.java
blob55763eeddfe3e0082b7f0bd873ee92ac84cda26e
1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
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., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 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.classpath.Configuration;
43 import gnu.java.io.ObjectIdentityWrapper;
44 import gnu.java.lang.reflect.TypeSignature;
45 import gnu.java.security.action.SetAccessibleAction;
47 import java.lang.reflect.Array;
48 import java.lang.reflect.Field;
49 import java.lang.reflect.InvocationTargetException;
50 import java.lang.reflect.Method;
51 import java.security.AccessController;
52 import java.util.Hashtable;
54 /**
55 * An <code>ObjectOutputStream</code> can be used to write objects
56 * as well as primitive data in a platform-independent manner to an
57 * <code>OutputStream</code>.
59 * The data produced by an <code>ObjectOutputStream</code> can be read
60 * and reconstituted by an <code>ObjectInputStream</code>.
62 * <code>writeObject (Object)</code> is used to write Objects, the
63 * <code>write&lt;type&gt;</code> methods are used to write primitive
64 * data (as in <code>DataOutputStream</code>). Strings can be written
65 * as objects or as primitive data.
67 * Not all objects can be written out using an
68 * <code>ObjectOutputStream</code>. Only those objects that are an
69 * instance of <code>java.io.Serializable</code> can be written.
71 * Using default serialization, information about the class of an
72 * object is written, all of the non-transient, non-static fields of
73 * the object are written, if any of these fields are objects, they are
74 * written out in the same manner.
76 * An object is only written out the first time it is encountered. If
77 * the object is encountered later, a reference to it is written to
78 * the underlying stream. Thus writing circular object graphs
79 * does not present a problem, nor are relationships between objects
80 * in a graph lost.
82 * Example usage:
83 * <pre>
84 * Hashtable map = new Hashtable ();
85 * map.put ("one", new Integer (1));
86 * map.put ("two", new Integer (2));
88 * ObjectOutputStream oos =
89 * new ObjectOutputStream (new FileOutputStream ("numbers"));
90 * oos.writeObject (map);
91 * oos.close ();
93 * ObjectInputStream ois =
94 * new ObjectInputStream (new FileInputStream ("numbers"));
95 * Hashtable newmap = (Hashtable)ois.readObject ();
97 * System.out.println (newmap);
98 * </pre>
100 * The default serialization can be overriden in two ways.
102 * By defining a method <code>private void
103 * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
104 * how information about itself is written.
105 * <code>defaultWriteObject ()</code> may be called from this method to
106 * carry out default serialization. This method is not
107 * responsible for dealing with fields of super-classes or subclasses.
109 * By implementing <code>java.io.Externalizable</code>. This gives
110 * the class complete control over the way it is written to the
111 * stream. If this approach is used the burden of writing superclass
112 * and subclass data is transfered to the class implementing
113 * <code>java.io.Externalizable</code>.
115 * @see java.io.DataOutputStream
116 * @see java.io.Externalizable
117 * @see java.io.ObjectInputStream
118 * @see java.io.Serializable
120 public class ObjectOutputStream extends OutputStream
121 implements ObjectOutput, ObjectStreamConstants
124 * Creates a new <code>ObjectOutputStream</code> that will do all of
125 * its writing onto <code>out</code>. This method also initializes
126 * the stream by writing the header information (stream magic number
127 * and stream version).
129 * @exception IOException Writing stream header to underlying
130 * stream cannot be completed.
132 * @see #writeStreamHeader()
134 public ObjectOutputStream (OutputStream out) throws IOException
136 realOutput = new DataOutputStream(out);
137 blockData = new byte[ BUFFER_SIZE ];
138 blockDataCount = 0;
139 blockDataOutput = new DataOutputStream(this);
140 setBlockDataMode(true);
141 replacementEnabled = false;
142 isSerializing = false;
143 nextOID = baseWireHandle;
144 OIDLookupTable = new Hashtable();
145 protocolVersion = defaultProtocolVersion;
146 useSubclassMethod = false;
147 writeStreamHeader();
149 if (Configuration.DEBUG)
151 String val = System.getProperty("gcj.dumpobjects");
152 if (val != null && !val.equals(""))
153 dump = true;
158 * Writes a representation of <code>obj</code> to the underlying
159 * output stream by writing out information about its class, then
160 * writing out each of the objects non-transient, non-static
161 * fields. If any of these fields are other objects,
162 * they are written out in the same manner.
164 * This method can be overriden by a class by implementing
165 * <code>private void writeObject (ObjectOutputStream)</code>.
167 * If an exception is thrown from this method, the stream is left in
168 * an undefined state.
170 * @exception NotSerializableException An attempt was made to
171 * serialize an <code>Object</code> that is not serializable.
173 * @exception InvalidClassException Somebody tried to serialize
174 * an object which is wrongly formatted.
176 * @exception IOException Exception from underlying
177 * <code>OutputStream</code>.
179 public final void writeObject(Object obj) throws IOException
181 if (useSubclassMethod)
183 if (dump)
184 dumpElementln ("WRITE OVERRIDE: " + obj);
186 writeObjectOverride(obj);
187 return;
190 if (dump)
191 dumpElementln ("WRITE: " + obj);
193 depth += 2;
195 boolean was_serializing = isSerializing;
196 boolean old_mode = setBlockDataMode(false);
199 isSerializing = true;
200 boolean replaceDone = false;
201 Object replacedObject = null;
203 while (true)
205 if (obj == null)
207 realOutput.writeByte(TC_NULL);
208 break;
211 Integer handle = findHandle(obj);
212 if (handle != null)
214 realOutput.writeByte(TC_REFERENCE);
215 realOutput.writeInt(handle.intValue());
216 break;
219 if (obj instanceof Class)
221 Class cl = (Class)obj;
222 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
223 realOutput.writeByte(TC_CLASS);
224 if (!osc.isProxyClass)
226 writeObject (osc);
228 else
230 realOutput.writeByte(TC_PROXYCLASSDESC);
231 Class[] intfs = cl.getInterfaces();
232 realOutput.writeInt(intfs.length);
233 for (int i = 0; i < intfs.length; i++)
234 realOutput.writeUTF(intfs[i].getName());
236 boolean oldmode = setBlockDataMode(true);
237 annotateProxyClass(cl);
238 setBlockDataMode(oldmode);
239 realOutput.writeByte(TC_ENDBLOCKDATA);
241 writeObject(osc.getSuper());
243 assignNewHandle(obj);
244 break;
247 if (obj instanceof ObjectStreamClass)
249 writeClassDescriptor((ObjectStreamClass) obj);
250 break;
253 Class clazz = obj.getClass();
254 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
255 if (osc == null)
256 throw new NotSerializableException(clazz.getName());
258 if ((replacementEnabled || obj instanceof Serializable)
259 && ! replaceDone)
261 replacedObject = obj;
263 if (obj instanceof Serializable)
267 Method m = osc.writeReplaceMethod;
268 if (m != null)
269 obj = m.invoke(obj, new Object[0]);
271 catch (IllegalAccessException ignore)
274 catch (InvocationTargetException ignore)
279 if (replacementEnabled)
280 obj = replaceObject(obj);
282 replaceDone = true;
283 continue;
286 if (obj instanceof String)
288 realOutput.writeByte(TC_STRING);
289 assignNewHandle(obj);
290 realOutput.writeUTF((String)obj);
291 break;
294 if (clazz.isArray ())
296 realOutput.writeByte(TC_ARRAY);
297 writeObject(osc);
298 assignNewHandle(obj);
299 writeArraySizeAndElements(obj, clazz.getComponentType());
300 break;
303 realOutput.writeByte(TC_OBJECT);
304 writeObject(osc);
306 if (replaceDone)
307 assignNewHandle(replacedObject);
308 else
309 assignNewHandle(obj);
311 if (obj instanceof Externalizable)
313 if (protocolVersion == PROTOCOL_VERSION_2)
314 setBlockDataMode(true);
316 ((Externalizable)obj).writeExternal(this);
318 if (protocolVersion == PROTOCOL_VERSION_2)
320 setBlockDataMode(false);
321 realOutput.writeByte(TC_ENDBLOCKDATA);
324 break;
327 if (obj instanceof Serializable)
329 Object prevObject = this.currentObject;
330 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
331 currentObject = obj;
332 ObjectStreamClass[] hierarchy =
333 ObjectStreamClass.getObjectStreamClasses(clazz);
335 for (int i = 0; i < hierarchy.length; i++)
337 currentObjectStreamClass = hierarchy[i];
339 fieldsAlreadyWritten = false;
340 if (currentObjectStreamClass.hasWriteMethod())
342 if (dump)
343 dumpElementln ("WRITE METHOD CALLED FOR: " + obj);
344 setBlockDataMode(true);
345 callWriteMethod(obj, currentObjectStreamClass);
346 setBlockDataMode(false);
347 realOutput.writeByte(TC_ENDBLOCKDATA);
348 if (dump)
349 dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj);
351 else
353 if (dump)
354 dumpElementln ("WRITE FIELDS CALLED FOR: " + obj);
355 writeFields(obj, currentObjectStreamClass);
359 this.currentObject = prevObject;
360 this.currentObjectStreamClass = prevObjectStreamClass;
361 currentPutField = null;
362 break;
365 throw new NotSerializableException(clazz.getName ());
366 } // end pseudo-loop
368 catch (ObjectStreamException ose)
370 // Rethrow these are fatal.
371 throw ose;
373 catch (IOException e)
375 realOutput.writeByte(TC_EXCEPTION);
376 reset(true);
378 setBlockDataMode(false);
381 if (Configuration.DEBUG)
383 e.printStackTrace(System.out);
385 writeObject(e);
387 catch (IOException ioe)
389 StreamCorruptedException ex =
390 new StreamCorruptedException
391 (ioe + " thrown while exception was being written to stream.");
392 if (Configuration.DEBUG)
394 ex.printStackTrace(System.out);
396 throw ex;
399 reset (true);
402 finally
404 isSerializing = was_serializing;
405 setBlockDataMode(old_mode);
406 depth -= 2;
408 if (dump)
409 dumpElementln ("END: " + obj);
413 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
415 realOutput.writeByte(TC_CLASSDESC);
416 realOutput.writeUTF(osc.getName());
417 realOutput.writeLong(osc.getSerialVersionUID());
418 assignNewHandle(osc);
420 int flags = osc.getFlags();
422 if (protocolVersion == PROTOCOL_VERSION_2
423 && osc.isExternalizable())
424 flags |= SC_BLOCK_DATA;
426 realOutput.writeByte(flags);
428 ObjectStreamField[] fields = osc.fields;
429 realOutput.writeShort(fields.length);
431 ObjectStreamField field;
432 for (int i = 0; i < fields.length; i++)
434 field = fields[i];
435 realOutput.writeByte(field.getTypeCode ());
436 realOutput.writeUTF(field.getName ());
438 if (! field.isPrimitive())
439 writeObject(field.getTypeString());
442 boolean oldmode = setBlockDataMode(true);
443 annotateClass(osc.forClass());
444 setBlockDataMode(oldmode);
445 realOutput.writeByte(TC_ENDBLOCKDATA);
447 if (osc.isSerializable() || osc.isExternalizable())
448 writeObject(osc.getSuper());
449 else
450 writeObject(null);
454 * Writes the current objects non-transient, non-static fields from
455 * the current class to the underlying output stream.
457 * This method is intended to be called from within a object's
458 * <code>private void writeObject (ObjectOutputStream)</code>
459 * method.
461 * @exception NotActiveException This method was called from a
462 * context other than from the current object's and current class's
463 * <code>private void writeObject (ObjectOutputStream)</code>
464 * method.
466 * @exception IOException Exception from underlying
467 * <code>OutputStream</code>.
469 public void defaultWriteObject()
470 throws IOException, NotActiveException
472 markFieldsWritten();
473 writeFields(currentObject, currentObjectStreamClass);
477 private void markFieldsWritten() throws IOException
479 if (currentObject == null || currentObjectStreamClass == null)
480 throw new NotActiveException
481 ("defaultWriteObject called by non-active class and/or object");
483 if (fieldsAlreadyWritten)
484 throw new IOException
485 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
487 fieldsAlreadyWritten = true;
491 * Resets stream to state equivalent to the state just after it was
492 * constructed.
494 * Causes all objects previously written to the stream to be
495 * forgotten. A notification of this reset is also written to the
496 * underlying stream.
498 * @exception IOException Exception from underlying
499 * <code>OutputStream</code> or reset called while serialization is
500 * in progress.
502 public void reset() throws IOException
504 reset(false);
508 private void reset(boolean internal) throws IOException
510 if (!internal)
512 if (isSerializing)
513 throw new IOException("Reset called while serialization in progress");
515 realOutput.writeByte(TC_RESET);
518 clearHandles();
523 * Informs this <code>ObjectOutputStream</code> to write data
524 * according to the specified protocol. There are currently two
525 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
526 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
527 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
528 * by the JDK 1.2.
530 * A non-portable method, <code>setDefaultProtocolVersion (int
531 * version)</code> is provided to change the default protocol
532 * version.
534 * For an explination of the differences beween the two protocols
535 * see XXX: the Java ObjectSerialization Specification.
537 * @exception IOException if <code>version</code> is not a valid
538 * protocol
540 * @see #setDefaultProtocolVersion(int)
542 public void useProtocolVersion(int version) throws IOException
544 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
545 throw new IOException("Invalid protocol version requested.");
547 protocolVersion = version;
552 * <em>GNU $classpath specific</em>
554 * Changes the default stream protocol used by all
555 * <code>ObjectOutputStream</code>s. There are currently two
556 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
557 * and <code>PROTOCOL_VERSION_2</code>. The default default is
558 * <code>PROTOCOL_VERSION_1</code>.
560 * @exception IOException if <code>version</code> is not a valid
561 * protocol
563 * @see #useProtocolVersion(int)
565 public static void setDefaultProtocolVersion(int version)
566 throws IOException
568 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
569 throw new IOException("Invalid protocol version requested.");
571 defaultProtocolVersion = version;
576 * An empty hook that allows subclasses to write extra information
577 * about classes to the stream. This method is called the first
578 * time each class is seen, and after all of the standard
579 * information about the class has been written.
581 * @exception IOException Exception from underlying
582 * <code>OutputStream</code>.
584 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
586 protected void annotateClass(Class cl) throws IOException
590 protected void annotateProxyClass(Class cl) throws IOException
595 * Allows subclasses to replace objects that are written to the
596 * stream with other objects to be written in their place. This
597 * method is called the first time each object is encountered
598 * (modulo reseting of the stream).
600 * This method must be enabled before it will be called in the
601 * serialization process.
603 * @exception IOException Exception from underlying
604 * <code>OutputStream</code>.
606 * @see #enableReplaceObject(boolean)
608 protected Object replaceObject(Object obj) throws IOException
610 return obj;
615 * If <code>enable</code> is <code>true</code> and this object is
616 * trusted, then <code>replaceObject (Object)</code> will be called
617 * in subsequent calls to <code>writeObject (Object)</code>.
618 * Otherwise, <code>replaceObject (Object)</code> will not be called.
620 * @exception SecurityException This class is not trusted.
622 protected boolean enableReplaceObject(boolean enable)
623 throws SecurityException
625 if (enable)
627 SecurityManager sm = System.getSecurityManager();
628 if (sm != null)
629 sm.checkPermission(new SerializablePermission("enableSubstitution"));
632 boolean old_val = replacementEnabled;
633 replacementEnabled = enable;
634 return old_val;
639 * Writes stream magic and stream version information to the
640 * underlying stream.
642 * @exception IOException Exception from underlying
643 * <code>OutputStream</code>.
645 protected void writeStreamHeader() throws IOException
647 realOutput.writeShort(STREAM_MAGIC);
648 realOutput.writeShort(STREAM_VERSION);
652 * Protected constructor that allows subclasses to override
653 * serialization. This constructor should be called by subclasses
654 * that wish to override <code>writeObject (Object)</code>. This
655 * method does a security check <i>NOTE: currently not
656 * implemented</i>, then sets a flag that informs
657 * <code>writeObject (Object)</code> to call the subclasses
658 * <code>writeObjectOverride (Object)</code> method.
660 * @see #writeObjectOverride(Object)
662 protected ObjectOutputStream() throws IOException, SecurityException
664 SecurityManager sec_man = System.getSecurityManager ();
665 if (sec_man != null)
666 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
667 useSubclassMethod = true;
672 * This method allows subclasses to override the default
673 * serialization mechanism provided by
674 * <code>ObjectOutputStream</code>. To make this method be used for
675 * writing objects, subclasses must invoke the 0-argument
676 * constructor on this class from there constructor.
678 * @see #ObjectOutputStream()
680 * @exception NotActiveException Subclass has arranged for this
681 * method to be called, but did not implement this method.
683 protected void writeObjectOverride(Object obj) throws NotActiveException,
684 IOException
686 throw new NotActiveException
687 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
692 * @see DataOutputStream#write(int)
694 public void write (int data) throws IOException
696 if (writeDataAsBlocks)
698 if (blockDataCount == BUFFER_SIZE)
699 drain();
701 blockData[ blockDataCount++ ] = (byte)data;
703 else
704 realOutput.write(data);
709 * @see DataOutputStream#write(byte[])
711 public void write(byte[] b) throws IOException
713 write(b, 0, b.length);
718 * @see DataOutputStream#write(byte[],int,int)
720 public void write(byte[] b, int off, int len) throws IOException
722 if (writeDataAsBlocks)
724 if (len < 0)
725 throw new IndexOutOfBoundsException();
727 if (blockDataCount + len < BUFFER_SIZE)
729 System.arraycopy(b, off, blockData, blockDataCount, len);
730 blockDataCount += len;
732 else
734 drain();
735 writeBlockDataHeader(len);
736 realOutput.write(b, off, len);
739 else
740 realOutput.write(b, off, len);
745 * @see DataOutputStream#flush()
747 public void flush () throws IOException
749 drain();
750 realOutput.flush();
755 * Causes the block-data buffer to be written to the underlying
756 * stream, but does not flush underlying stream.
758 * @exception IOException Exception from underlying
759 * <code>OutputStream</code>.
761 protected void drain() throws IOException
763 if (blockDataCount == 0)
764 return;
766 if (writeDataAsBlocks)
767 writeBlockDataHeader(blockDataCount);
768 realOutput.write(blockData, 0, blockDataCount);
769 blockDataCount = 0;
774 * @see java.io.DataOutputStream#close ()
776 public void close() throws IOException
778 flush();
779 realOutput.close();
784 * @see java.io.DataOutputStream#writeBoolean (boolean)
786 public void writeBoolean(boolean data) throws IOException
788 blockDataOutput.writeBoolean(data);
793 * @see java.io.DataOutputStream#writeByte (int)
795 public void writeByte(int data) throws IOException
797 blockDataOutput.writeByte(data);
802 * @see java.io.DataOutputStream#writeShort (int)
804 public void writeShort (int data) throws IOException
806 blockDataOutput.writeShort(data);
811 * @see java.io.DataOutputStream#writeChar (int)
813 public void writeChar(int data) throws IOException
815 blockDataOutput.writeChar(data);
820 * @see java.io.DataOutputStream#writeInt (int)
822 public void writeInt(int data) throws IOException
824 blockDataOutput.writeInt(data);
829 * @see java.io.DataOutputStream#writeLong (long)
831 public void writeLong(long data) throws IOException
833 blockDataOutput.writeLong(data);
838 * @see java.io.DataOutputStream#writeFloat (float)
840 public void writeFloat(float data) throws IOException
842 blockDataOutput.writeFloat(data);
847 * @see java.io.DataOutputStream#writeDouble (double)
849 public void writeDouble(double data) throws IOException
851 blockDataOutput.writeDouble(data);
856 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
858 public void writeBytes(String data) throws IOException
860 blockDataOutput.writeBytes(data);
865 * @see java.io.DataOutputStream#writeChars (java.lang.String)
867 public void writeChars(String data) throws IOException
869 dataOutput.writeChars(data);
874 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
876 public void writeUTF(String data) throws IOException
878 dataOutput.writeUTF(data);
883 * This class allows a class to specify exactly which fields should
884 * be written, and what values should be written for these fields.
886 * XXX: finish up comments
888 public abstract static class PutField
890 public abstract void put (String name, boolean value);
891 public abstract void put (String name, byte value);
892 public abstract void put (String name, char value);
893 public abstract void put (String name, double value);
894 public abstract void put (String name, float value);
895 public abstract void put (String name, int value);
896 public abstract void put (String name, long value);
897 public abstract void put (String name, short value);
898 public abstract void put (String name, Object value);
901 * @deprecated
903 public abstract void write (ObjectOutput out) throws IOException;
906 public PutField putFields() throws IOException
908 if (currentPutField != null)
909 return currentPutField;
911 currentPutField = new PutField()
913 private byte[] prim_field_data
914 = new byte[currentObjectStreamClass.primFieldSize];
915 private Object[] objs
916 = new Object[currentObjectStreamClass.objectFieldCount];
918 private ObjectStreamField getField (String name)
920 ObjectStreamField field
921 = currentObjectStreamClass.getField(name);
923 if (field == null)
924 throw new IllegalArgumentException("no such serializable field " + name);
926 return field;
929 public void put(String name, boolean value)
931 ObjectStreamField field = getField(name);
933 checkType(field, 'Z');
934 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
937 public void put(String name, byte value)
939 ObjectStreamField field = getField(name);
941 checkType(field, 'B');
942 prim_field_data[field.getOffset()] = value;
945 public void put(String name, char value)
947 ObjectStreamField field = getField(name);
949 checkType(field, 'C');
950 int off = field.getOffset();
951 prim_field_data[off++] = (byte)(value >>> 8);
952 prim_field_data[off] = (byte)value;
955 public void put(String name, double value)
957 ObjectStreamField field = getField (name);
959 checkType(field, 'D');
960 int off = field.getOffset();
961 long l_value = Double.doubleToLongBits (value);
962 prim_field_data[off++] = (byte)(l_value >>> 52);
963 prim_field_data[off++] = (byte)(l_value >>> 48);
964 prim_field_data[off++] = (byte)(l_value >>> 40);
965 prim_field_data[off++] = (byte)(l_value >>> 32);
966 prim_field_data[off++] = (byte)(l_value >>> 24);
967 prim_field_data[off++] = (byte)(l_value >>> 16);
968 prim_field_data[off++] = (byte)(l_value >>> 8);
969 prim_field_data[off] = (byte)l_value;
972 public void put(String name, float value)
974 ObjectStreamField field = getField(name);
976 checkType(field, 'F');
977 int off = field.getOffset();
978 int i_value = Float.floatToIntBits(value);
979 prim_field_data[off++] = (byte)(i_value >>> 24);
980 prim_field_data[off++] = (byte)(i_value >>> 16);
981 prim_field_data[off++] = (byte)(i_value >>> 8);
982 prim_field_data[off] = (byte)i_value;
985 public void put(String name, int value)
987 ObjectStreamField field = getField(name);
988 checkType(field, 'I');
989 int off = field.getOffset();
990 prim_field_data[off++] = (byte)(value >>> 24);
991 prim_field_data[off++] = (byte)(value >>> 16);
992 prim_field_data[off++] = (byte)(value >>> 8);
993 prim_field_data[off] = (byte)value;
996 public void put(String name, long value)
998 ObjectStreamField field = getField(name);
999 checkType(field, 'J');
1000 int off = field.getOffset();
1001 prim_field_data[off++] = (byte)(value >>> 52);
1002 prim_field_data[off++] = (byte)(value >>> 48);
1003 prim_field_data[off++] = (byte)(value >>> 40);
1004 prim_field_data[off++] = (byte)(value >>> 32);
1005 prim_field_data[off++] = (byte)(value >>> 24);
1006 prim_field_data[off++] = (byte)(value >>> 16);
1007 prim_field_data[off++] = (byte)(value >>> 8);
1008 prim_field_data[off] = (byte)value;
1011 public void put(String name, short value)
1013 ObjectStreamField field = getField(name);
1014 checkType(field, 'S');
1015 int off = field.getOffset();
1016 prim_field_data[off++] = (byte)(value >>> 8);
1017 prim_field_data[off] = (byte)value;
1020 public void put(String name, Object value)
1022 ObjectStreamField field = getField(name);
1024 if (value != null &&
1025 ! field.getType().isAssignableFrom(value.getClass ()))
1026 throw new IllegalArgumentException("Class " + value.getClass() +
1027 " cannot be cast to " + field.getType());
1028 objs[field.getOffset()] = value;
1031 public void write(ObjectOutput out) throws IOException
1033 // Apparently Block data is not used with PutField as per
1034 // empirical evidence against JDK 1.2. Also see Mauve test
1035 // java.io.ObjectInputOutput.Test.GetPutField.
1036 boolean oldmode = setBlockDataMode(false);
1037 out.write(prim_field_data);
1038 for (int i = 0; i < objs.length; ++ i)
1039 out.writeObject(objs[i]);
1040 setBlockDataMode(oldmode);
1043 private void checkType(ObjectStreamField field, char type)
1044 throws IllegalArgumentException
1046 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1047 != type)
1048 throw new IllegalArgumentException();
1051 // end PutFieldImpl
1053 return currentPutField;
1057 public void writeFields() throws IOException
1059 if (currentPutField == null)
1060 throw new NotActiveException("writeFields can only be called after putFields has been called");
1062 markFieldsWritten();
1063 currentPutField.write(this);
1067 // write out the block-data buffer, picking the correct header
1068 // depending on the size of the buffer
1069 private void writeBlockDataHeader(int size) throws IOException
1071 if (size < 256)
1073 realOutput.writeByte(TC_BLOCKDATA);
1074 realOutput.write(size);
1076 else
1078 realOutput.writeByte(TC_BLOCKDATALONG);
1079 realOutput.writeInt(size);
1084 // lookup the handle for OBJ, return null if OBJ doesn't have a
1085 // handle yet
1086 private Integer findHandle(Object obj)
1088 return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj));
1092 // assigns the next availible handle to OBJ
1093 private int assignNewHandle(Object obj)
1095 OIDLookupTable.put(new ObjectIdentityWrapper(obj),
1096 new Integer(nextOID));
1097 return nextOID++;
1101 // resets mapping from objects to handles
1102 private void clearHandles()
1104 nextOID = baseWireHandle;
1105 OIDLookupTable.clear();
1109 // write out array size followed by each element of the array
1110 private void writeArraySizeAndElements(Object array, Class clazz)
1111 throws IOException
1113 int length = Array.getLength(array);
1115 if (clazz.isPrimitive())
1117 if (clazz == Boolean.TYPE)
1119 boolean[] cast_array = (boolean[])array;
1120 realOutput.writeInt (length);
1121 for (int i = 0; i < length; i++)
1122 realOutput.writeBoolean(cast_array[i]);
1123 return;
1125 if (clazz == Byte.TYPE)
1127 byte[] cast_array = (byte[])array;
1128 realOutput.writeInt(length);
1129 realOutput.write(cast_array, 0, length);
1130 return;
1132 if (clazz == Character.TYPE)
1134 char[] cast_array = (char[])array;
1135 realOutput.writeInt(length);
1136 for (int i = 0; i < length; i++)
1137 realOutput.writeChar(cast_array[i]);
1138 return;
1140 if (clazz == Double.TYPE)
1142 double[] cast_array = (double[])array;
1143 realOutput.writeInt(length);
1144 for (int i = 0; i < length; i++)
1145 realOutput.writeDouble(cast_array[i]);
1146 return;
1148 if (clazz == Float.TYPE)
1150 float[] cast_array = (float[])array;
1151 realOutput.writeInt(length);
1152 for (int i = 0; i < length; i++)
1153 realOutput.writeFloat(cast_array[i]);
1154 return;
1156 if (clazz == Integer.TYPE)
1158 int[] cast_array = (int[])array;
1159 realOutput.writeInt(length);
1160 for (int i = 0; i < length; i++)
1161 realOutput.writeInt(cast_array[i]);
1162 return;
1164 if (clazz == Long.TYPE)
1166 long[] cast_array = (long[])array;
1167 realOutput.writeInt (length);
1168 for (int i = 0; i < length; i++)
1169 realOutput.writeLong(cast_array[i]);
1170 return;
1172 if (clazz == Short.TYPE)
1174 short[] cast_array = (short[])array;
1175 realOutput.writeInt (length);
1176 for (int i = 0; i < length; i++)
1177 realOutput.writeShort(cast_array[i]);
1178 return;
1181 else
1183 Object[] cast_array = (Object[])array;
1184 realOutput.writeInt(length);
1185 for (int i = 0; i < length; i++)
1186 writeObject(cast_array[i]);
1191 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1192 // FIELDS are already in canonical order.
1193 private void writeFields(Object obj, ObjectStreamClass osc)
1194 throws IOException
1196 ObjectStreamField[] fields = osc.fields;
1197 boolean oldmode = setBlockDataMode(false);
1198 String field_name;
1199 Class type;
1201 for (int i = 0; i < fields.length; i++)
1203 field_name = fields[i].getName();
1204 type = fields[i].getType();
1206 if (dump)
1207 dumpElementln ("WRITE FIELD: " + field_name + " type=" + type);
1209 if (type == Boolean.TYPE)
1210 realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name));
1211 else if (type == Byte.TYPE)
1212 realOutput.writeByte(getByteField(obj, osc.forClass(), field_name));
1213 else if (type == Character.TYPE)
1214 realOutput.writeChar(getCharField(obj, osc.forClass(), field_name));
1215 else if (type == Double.TYPE)
1216 realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name));
1217 else if (type == Float.TYPE)
1218 realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name));
1219 else if (type == Integer.TYPE)
1220 realOutput.writeInt(getIntField(obj, osc.forClass(), field_name));
1221 else if (type == Long.TYPE)
1222 realOutput.writeLong(getLongField(obj, osc.forClass(), field_name));
1223 else if (type == Short.TYPE)
1224 realOutput.writeShort(getShortField(obj, osc.forClass(), field_name));
1225 else
1226 writeObject(getObjectField(obj, osc.forClass(), field_name,
1227 fields[i].getTypeString ()));
1229 setBlockDataMode(oldmode);
1233 // Toggles writing primitive data to block-data buffer.
1234 // Package-private to avoid a trampoline constructor.
1235 boolean setBlockDataMode(boolean on) throws IOException
1237 if (on == writeDataAsBlocks)
1238 return on;
1240 drain();
1241 boolean oldmode = writeDataAsBlocks;
1242 writeDataAsBlocks = on;
1244 if (on)
1245 dataOutput = blockDataOutput;
1246 else
1247 dataOutput = realOutput;
1249 return oldmode;
1253 private void callWriteMethod(Object obj, ObjectStreamClass osc)
1254 throws IOException
1256 currentPutField = null;
1259 Object args[] = {this};
1260 osc.writeObjectMethod.invoke(obj, args);
1262 catch (InvocationTargetException x)
1264 /* Rethrow if possible. */
1265 Throwable exception = x.getTargetException();
1266 if (exception instanceof RuntimeException)
1267 throw (RuntimeException) exception;
1268 if (exception instanceof IOException)
1269 throw (IOException) exception;
1271 IOException ioe
1272 = new IOException("Exception thrown from writeObject() on " +
1273 osc.forClass().getName() + ": " +
1274 exception.getClass().getName());
1275 ioe.initCause(exception);
1276 throw ioe;
1278 catch (Exception x)
1280 IOException ioe
1281 = new IOException("Failure invoking writeObject() on " +
1282 osc.forClass().getName() + ": " +
1283 x.getClass().getName());
1284 ioe.initCause(x);
1285 throw ioe;
1289 private boolean getBooleanField(Object obj, Class klass, String field_name)
1290 throws IOException
1294 Field f = getField(klass, field_name);
1295 boolean b = f.getBoolean(obj);
1296 return b;
1298 catch (IllegalArgumentException _)
1300 throw new InvalidClassException
1301 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1303 catch (IOException e)
1305 throw e;
1307 catch (Exception _)
1309 throw new IOException("Unexpected exception " + _);
1313 private byte getByteField (Object obj, Class klass, String field_name)
1314 throws IOException
1318 Field f = getField (klass, field_name);
1319 byte b = f.getByte (obj);
1320 return b;
1322 catch (IllegalArgumentException _)
1324 throw new InvalidClassException
1325 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1327 catch (IOException e)
1329 throw e;
1331 catch (Exception _)
1333 throw new IOException("Unexpected exception " + _);
1337 private char getCharField (Object obj, Class klass, String field_name)
1338 throws IOException
1342 Field f = getField (klass, field_name);
1343 char b = f.getChar (obj);
1344 return b;
1346 catch (IllegalArgumentException _)
1348 throw new InvalidClassException
1349 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1351 catch (IOException e)
1353 throw e;
1355 catch (Exception _)
1357 throw new IOException("Unexpected exception " + _);
1361 private double getDoubleField (Object obj, Class klass, String field_name)
1362 throws IOException
1366 Field f = getField (klass, field_name);
1367 double b = f.getDouble (obj);
1368 return b;
1370 catch (IllegalArgumentException _)
1372 throw new InvalidClassException
1373 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1375 catch (IOException e)
1377 throw e;
1379 catch (Exception _)
1381 throw new IOException("Unexpected exception " + _);
1385 private float getFloatField (Object obj, Class klass, String field_name)
1386 throws IOException
1390 Field f = getField (klass, field_name);
1391 float b = f.getFloat (obj);
1392 return b;
1394 catch (IllegalArgumentException _)
1396 throw new InvalidClassException
1397 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1399 catch (IOException e)
1401 throw e;
1403 catch (Exception _)
1405 throw new IOException("Unexpected exception " + _);
1409 private int getIntField (Object obj, Class klass, String field_name)
1410 throws IOException
1414 Field f = getField (klass, field_name);
1415 int b = f.getInt (obj);
1416 return b;
1418 catch (IllegalArgumentException _)
1420 throw new InvalidClassException
1421 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1423 catch (IOException e)
1425 throw e;
1427 catch (Exception _)
1429 throw new IOException("Unexpected exception " + _);
1433 private long getLongField (Object obj, Class klass, String field_name)
1434 throws IOException
1438 Field f = getField (klass, field_name);
1439 long b = f.getLong (obj);
1440 return b;
1442 catch (IllegalArgumentException _)
1444 throw new InvalidClassException
1445 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1447 catch (IOException e)
1449 throw e;
1451 catch (Exception _)
1453 throw new IOException("Unexpected exception " + _);
1457 private short getShortField (Object obj, Class klass, String field_name)
1458 throws IOException
1462 Field f = getField (klass, field_name);
1463 short b = f.getShort (obj);
1464 return b;
1466 catch (IllegalArgumentException _)
1468 throw new InvalidClassException
1469 ("invalid requested type for field " + field_name + " in class " + klass.getName());
1471 catch (IOException e)
1473 throw e;
1475 catch (Exception _)
1477 throw new IOException("Unexpected exception " + _);
1481 private Object getObjectField (Object obj, Class klass, String field_name,
1482 String type_code) throws IOException
1486 Field f = getField (klass, field_name);
1487 ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType());
1489 if (of.getTypeString() == null ||
1490 !of.getTypeString().equals(type_code))
1491 throw new InvalidClassException
1492 ("invalid type code for " + field_name + " in class " + klass.getName());
1494 Object o = f.get (obj);
1495 // FIXME: We should check the type_code here
1496 return o;
1498 catch (IOException e)
1500 throw e;
1502 catch (Exception e)
1504 throw new IOException ();
1508 private Field getField (Class klass, String name)
1509 throws java.io.InvalidClassException
1513 final Field f = klass.getDeclaredField(name);
1514 setAccessible.setMember(f);
1515 AccessController.doPrivileged(setAccessible);
1516 return f;
1518 catch (java.lang.NoSuchFieldException e)
1520 throw new InvalidClassException
1521 ("no field called " + name + " in class " + klass.getName());
1525 private void dumpElementln (String msg)
1527 for (int i = 0; i < depth; i++)
1528 System.out.print (" ");
1529 System.out.print (Thread.currentThread() + ": ");
1530 System.out.println(msg);
1533 // this value comes from 1.2 spec, but is used in 1.1 as well
1534 private static final int BUFFER_SIZE = 1024;
1536 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1538 private DataOutputStream dataOutput;
1539 private boolean writeDataAsBlocks;
1540 private DataOutputStream realOutput;
1541 private DataOutputStream blockDataOutput;
1542 private byte[] blockData;
1543 private int blockDataCount;
1544 private Object currentObject;
1545 // Package-private to avoid a trampoline.
1546 ObjectStreamClass currentObjectStreamClass;
1547 private PutField currentPutField;
1548 private boolean fieldsAlreadyWritten;
1549 private boolean replacementEnabled;
1550 private boolean isSerializing;
1551 private int nextOID;
1552 private Hashtable OIDLookupTable;
1553 private int protocolVersion;
1554 private boolean useSubclassMethod;
1555 private SetAccessibleAction setAccessible = new SetAccessibleAction();
1557 // The nesting depth for debugging output
1558 private int depth = 0;
1560 // Set if we're generating debugging dumps
1561 private boolean dump = false;
1563 static
1565 if (Configuration.INIT_LOAD_LIBRARY)
1567 System.loadLibrary("javaio");