libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / java / io / ObjectOutputStream.java
blob303aed472d9bed14281e3c6b558ad5596ba41792
1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
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.ObjectIdentityMap2Int;
43 import gnu.java.lang.reflect.TypeSignature;
44 import gnu.java.security.action.SetAccessibleAction;
46 import java.lang.reflect.Array;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.InvocationTargetException;
49 import java.lang.reflect.Method;
52 /**
53 * An <code>ObjectOutputStream</code> can be used to write objects
54 * as well as primitive data in a platform-independent manner to an
55 * <code>OutputStream</code>.
57 * The data produced by an <code>ObjectOutputStream</code> can be read
58 * and reconstituted by an <code>ObjectInputStream</code>.
60 * <code>writeObject (Object)</code> is used to write Objects, the
61 * <code>write&lt;type&gt;</code> methods are used to write primitive
62 * data (as in <code>DataOutputStream</code>). Strings can be written
63 * as objects or as primitive data.
65 * Not all objects can be written out using an
66 * <code>ObjectOutputStream</code>. Only those objects that are an
67 * instance of <code>java.io.Serializable</code> can be written.
69 * Using default serialization, information about the class of an
70 * object is written, all of the non-transient, non-static fields of
71 * the object are written, if any of these fields are objects, they are
72 * written out in the same manner.
74 * An object is only written out the first time it is encountered. If
75 * the object is encountered later, a reference to it is written to
76 * the underlying stream. Thus writing circular object graphs
77 * does not present a problem, nor are relationships between objects
78 * in a graph lost.
80 * Example usage:
81 * <pre>
82 * Hashtable map = new Hashtable ();
83 * map.put ("one", new Integer (1));
84 * map.put ("two", new Integer (2));
86 * ObjectOutputStream oos =
87 * new ObjectOutputStream (new FileOutputStream ("numbers"));
88 * oos.writeObject (map);
89 * oos.close ();
91 * ObjectInputStream ois =
92 * new ObjectInputStream (new FileInputStream ("numbers"));
93 * Hashtable newmap = (Hashtable)ois.readObject ();
95 * System.out.println (newmap);
96 * </pre>
98 * The default serialization can be overriden in two ways.
100 * By defining a method <code>private void
101 * writeObject (ObjectOutputStream)</code>, a class can dictate exactly
102 * how information about itself is written.
103 * <code>defaultWriteObject ()</code> may be called from this method to
104 * carry out default serialization. This method is not
105 * responsible for dealing with fields of super-classes or subclasses.
107 * By implementing <code>java.io.Externalizable</code>. This gives
108 * the class complete control over the way it is written to the
109 * stream. If this approach is used the burden of writing superclass
110 * and subclass data is transfered to the class implementing
111 * <code>java.io.Externalizable</code>.
113 * @see java.io.DataOutputStream
114 * @see java.io.Externalizable
115 * @see java.io.ObjectInputStream
116 * @see java.io.Serializable
117 * @author Tom Tromey (tromey@redhat.com)
118 * @author Jeroen Frijters (jeroen@frijters.net)
119 * @author Guilhem Lavaux (guilhem@kaffe.org)
120 * @author Michael Koch (konqueror@gmx.de)
121 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
123 public class ObjectOutputStream extends OutputStream
124 implements ObjectOutput, ObjectStreamConstants
127 * Creates a new <code>ObjectOutputStream</code> that will do all of
128 * its writing onto <code>out</code>. This method also initializes
129 * the stream by writing the header information (stream magic number
130 * and stream version).
132 * @exception IOException Writing stream header to underlying
133 * stream cannot be completed.
135 * @see #writeStreamHeader()
137 public ObjectOutputStream (OutputStream out) throws IOException
139 realOutput = new DataOutputStream(out);
140 blockData = new byte[ BUFFER_SIZE ];
141 blockDataCount = 0;
142 blockDataOutput = new DataOutputStream(this);
143 setBlockDataMode(true);
144 replacementEnabled = false;
145 isSerializing = false;
146 nextOID = baseWireHandle;
147 OIDLookupTable = new ObjectIdentityMap2Int();
148 protocolVersion = defaultProtocolVersion;
149 useSubclassMethod = false;
150 writeStreamHeader();
152 if (DEBUG)
154 String val = System.getProperty("gcj.dumpobjects");
155 if (val != null && !val.equals(""))
156 dump = true;
161 * Writes a representation of <code>obj</code> to the underlying
162 * output stream by writing out information about its class, then
163 * writing out each of the objects non-transient, non-static
164 * fields. If any of these fields are other objects,
165 * they are written out in the same manner.
167 * This method can be overriden by a class by implementing
168 * <code>private void writeObject (ObjectOutputStream)</code>.
170 * If an exception is thrown from this method, the stream is left in
171 * an undefined state.
173 * @param obj the object to serialize.
174 * @exception NotSerializableException An attempt was made to
175 * serialize an <code>Object</code> that is not serializable.
177 * @exception InvalidClassException Somebody tried to serialize
178 * an object which is wrongly formatted.
180 * @exception IOException Exception from underlying
181 * <code>OutputStream</code>.
182 * @see #writeUnshared(Object)
184 public final void writeObject(Object obj) throws IOException
186 writeObject(obj, true);
190 * Writes an object to the stream in the same manner as
191 * {@link #writeObject(Object)}, but without the use of
192 * references. As a result, the object is always written
193 * to the stream in full. Likewise, if an object is written
194 * by this method and is then later written again by
195 * {@link #writeObject(Object)}, both calls will write out
196 * the object in full, as the later call to
197 * {@link #writeObject(Object)} will know nothing of the
198 * earlier use of {@link #writeUnshared(Object)}.
200 * @param obj the object to serialize.
201 * @throws NotSerializableException if the object being
202 * serialized does not implement
203 * {@link Serializable}.
204 * @throws InvalidClassException if a problem occurs with
205 * the class of the object being
206 * serialized.
207 * @throws IOException if an I/O error occurs on the underlying
208 * <code>OutputStream</code>.
209 * @since 1.4
210 * @see #writeObject(Object)
212 public void writeUnshared(Object obj)
213 throws IOException
215 writeObject(obj, false);
219 * Writes a representation of <code>obj</code> to the underlying
220 * output stream by writing out information about its class, then
221 * writing out each of the objects non-transient, non-static
222 * fields. If any of these fields are other objects,
223 * they are written out in the same manner.
225 * This method can be overriden by a class by implementing
226 * <code>private void writeObject (ObjectOutputStream)</code>.
228 * If an exception is thrown from this method, the stream is left in
229 * an undefined state.
231 * @param obj the object to serialize.
232 * @param shared true if the serialized object should be
233 * shared with later calls.
234 * @exception NotSerializableException An attempt was made to
235 * serialize an <code>Object</code> that is not serializable.
237 * @exception InvalidClassException Somebody tried to serialize
238 * an object which is wrongly formatted.
240 * @exception IOException Exception from underlying
241 * <code>OutputStream</code>.
242 * @see #writeUnshared(Object)
244 private final void writeObject(Object obj, boolean shared)
245 throws IOException
247 if (useSubclassMethod)
249 if (dump)
250 dumpElementln ("WRITE OVERRIDE: " + obj);
252 writeObjectOverride(obj);
253 return;
256 if (dump)
257 dumpElementln ("WRITE: ", obj);
259 depth += 2;
261 boolean was_serializing = isSerializing;
262 boolean old_mode = setBlockDataMode(false);
265 isSerializing = true;
266 boolean replaceDone = false;
267 Object replacedObject = null;
269 while (true)
271 if (obj == null)
273 realOutput.writeByte(TC_NULL);
274 break;
277 int handle = findHandle(obj);
278 if (handle >= 0 && shared)
280 realOutput.writeByte(TC_REFERENCE);
281 realOutput.writeInt(handle);
282 break;
285 if (obj instanceof Class)
287 Class cl = (Class)obj;
288 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
289 realOutput.writeByte(TC_CLASS);
290 if (!osc.isProxyClass)
292 writeObject (osc);
294 else
295 {System.err.println("1");
296 realOutput.writeByte(TC_PROXYCLASSDESC);
297 Class[] intfs = cl.getInterfaces();
298 realOutput.writeInt(intfs.length);
299 for (int i = 0; i < intfs.length; i++)
300 realOutput.writeUTF(intfs[i].getName());
302 boolean oldmode = setBlockDataMode(true);
303 annotateProxyClass(cl);
304 setBlockDataMode(oldmode);
305 realOutput.writeByte(TC_ENDBLOCKDATA);
307 writeObject(osc.getSuper());
309 if (shared)
310 assignNewHandle(obj);
311 break;
314 if (obj instanceof ObjectStreamClass)
316 writeClassDescriptor((ObjectStreamClass) obj);
317 break;
320 Class clazz = obj.getClass();
321 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
322 if (osc == null)
323 throw new NotSerializableException(clazz.getName());
325 if (osc.isEnum())
327 /* TC_ENUM classDesc newHandle enumConstantName */
328 realOutput.writeByte(TC_ENUM);
329 writeObject(osc);
330 if (shared)
331 assignNewHandle(obj);
332 writeObject(((Enum) obj).name());
333 break;
336 if ((replacementEnabled || obj instanceof Serializable)
337 && ! replaceDone)
339 replacedObject = obj;
341 if (obj instanceof Serializable)
345 Method m = osc.writeReplaceMethod;
346 if (m != null)
347 obj = m.invoke(obj, new Object[0]);
349 catch (IllegalAccessException ignore)
352 catch (InvocationTargetException ignore)
357 if (replacementEnabled)
358 obj = replaceObject(obj);
360 replaceDone = true;
361 continue;
364 if (obj instanceof String)
366 String s = (String)obj;
367 long l = realOutput.getUTFlength(s, 0, 0);
368 if (l <= 65535)
370 realOutput.writeByte(TC_STRING);
371 if (shared)
372 assignNewHandle(obj);
373 realOutput.writeUTFShort(s, (int)l);
375 else
377 realOutput.writeByte(TC_LONGSTRING);
378 if (shared)
379 assignNewHandle(obj);
380 realOutput.writeUTFLong(s, l);
382 break;
385 if (clazz.isArray ())
387 realOutput.writeByte(TC_ARRAY);
388 writeObject(osc);
389 if (shared)
390 assignNewHandle(obj);
391 writeArraySizeAndElements(obj, clazz.getComponentType());
392 break;
395 realOutput.writeByte(TC_OBJECT);
396 writeObject(osc);
398 if (shared)
399 if (replaceDone)
400 assignNewHandle(replacedObject);
401 else
402 assignNewHandle(obj);
404 if (obj instanceof Externalizable)
406 if (protocolVersion == PROTOCOL_VERSION_2)
407 setBlockDataMode(true);
409 ((Externalizable)obj).writeExternal(this);
411 if (protocolVersion == PROTOCOL_VERSION_2)
413 setBlockDataMode(false);
414 realOutput.writeByte(TC_ENDBLOCKDATA);
417 break;
420 if (obj instanceof Serializable)
422 Object prevObject = this.currentObject;
423 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
424 currentObject = obj;
425 ObjectStreamClass[] hierarchy = osc.hierarchy();
427 for (int i = 0; i < hierarchy.length; i++)
429 currentObjectStreamClass = hierarchy[i];
431 fieldsAlreadyWritten = false;
432 if (currentObjectStreamClass.hasWriteMethod())
434 if (dump)
435 dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
436 setBlockDataMode(true);
437 callWriteMethod(obj, currentObjectStreamClass);
438 setBlockDataMode(false);
439 realOutput.writeByte(TC_ENDBLOCKDATA);
440 if (dump)
441 dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
443 else
445 if (dump)
446 dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
447 writeFields(obj, currentObjectStreamClass);
451 this.currentObject = prevObject;
452 this.currentObjectStreamClass = prevObjectStreamClass;
453 currentPutField = null;
454 break;
457 throw new NotSerializableException(clazz.getName()
458 + " in "
459 + obj.getClass());
460 } // end pseudo-loop
462 catch (ObjectStreamException ose)
464 // Rethrow these are fatal.
465 throw ose;
467 catch (IOException e)
469 realOutput.writeByte(TC_EXCEPTION);
470 reset(true);
472 setBlockDataMode(false);
475 if (DEBUG)
477 e.printStackTrace(System.out);
479 writeObject(e);
481 catch (IOException ioe)
483 StreamCorruptedException ex =
484 new StreamCorruptedException
485 (ioe + " thrown while exception was being written to stream.");
486 if (DEBUG)
488 ex.printStackTrace(System.out);
490 throw ex;
493 reset (true);
496 finally
498 isSerializing = was_serializing;
499 setBlockDataMode(old_mode);
500 depth -= 2;
502 if (dump)
503 dumpElementln ("END: ", obj);
507 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
509 if (osc.isProxyClass)
511 realOutput.writeByte(TC_PROXYCLASSDESC);
512 Class[] intfs = osc.forClass().getInterfaces();
513 realOutput.writeInt(intfs.length);
514 for (int i = 0; i < intfs.length; i++)
515 realOutput.writeUTF(intfs[i].getName());
517 assignNewHandle(osc);
519 boolean oldmode = setBlockDataMode(true);
520 annotateProxyClass(osc.forClass());
521 setBlockDataMode(oldmode);
522 realOutput.writeByte(TC_ENDBLOCKDATA);
524 else
526 realOutput.writeByte(TC_CLASSDESC);
527 realOutput.writeUTF(osc.getName());
528 if (osc.isEnum())
529 realOutput.writeLong(0L);
530 else
531 realOutput.writeLong(osc.getSerialVersionUID());
532 assignNewHandle(osc);
534 int flags = osc.getFlags();
536 if (protocolVersion == PROTOCOL_VERSION_2
537 && osc.isExternalizable())
538 flags |= SC_BLOCK_DATA;
540 realOutput.writeByte(flags);
542 ObjectStreamField[] fields = osc.fields;
544 if (fields == ObjectStreamClass.INVALID_FIELDS)
545 throw new InvalidClassException
546 (osc.getName(), "serialPersistentFields is invalid");
548 realOutput.writeShort(fields.length);
550 ObjectStreamField field;
551 for (int i = 0; i < fields.length; i++)
553 field = fields[i];
554 realOutput.writeByte(field.getTypeCode ());
555 realOutput.writeUTF(field.getName ());
557 if (! field.isPrimitive())
558 writeObject(field.getTypeString());
561 boolean oldmode = setBlockDataMode(true);
562 annotateClass(osc.forClass());
563 setBlockDataMode(oldmode);
564 realOutput.writeByte(TC_ENDBLOCKDATA);
567 if (osc.isSerializable() || osc.isExternalizable())
568 writeObject(osc.getSuper());
569 else
570 writeObject(null);
574 * Writes the current objects non-transient, non-static fields from
575 * the current class to the underlying output stream.
577 * This method is intended to be called from within a object's
578 * <code>private void writeObject (ObjectOutputStream)</code>
579 * method.
581 * @exception NotActiveException This method was called from a
582 * context other than from the current object's and current class's
583 * <code>private void writeObject (ObjectOutputStream)</code>
584 * method.
586 * @exception IOException Exception from underlying
587 * <code>OutputStream</code>.
589 public void defaultWriteObject()
590 throws IOException, NotActiveException
592 markFieldsWritten();
593 writeFields(currentObject, currentObjectStreamClass);
597 private void markFieldsWritten() throws IOException
599 if (currentObject == null || currentObjectStreamClass == null)
600 throw new NotActiveException
601 ("defaultWriteObject called by non-active class and/or object");
603 if (fieldsAlreadyWritten)
604 throw new IOException
605 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
607 fieldsAlreadyWritten = true;
611 * Resets stream to state equivalent to the state just after it was
612 * constructed.
614 * Causes all objects previously written to the stream to be
615 * forgotten. A notification of this reset is also written to the
616 * underlying stream.
618 * @exception IOException Exception from underlying
619 * <code>OutputStream</code> or reset called while serialization is
620 * in progress.
622 public void reset() throws IOException
624 reset(false);
628 private void reset(boolean internal) throws IOException
630 if (!internal)
632 if (isSerializing)
633 throw new IOException("Reset called while serialization in progress");
635 realOutput.writeByte(TC_RESET);
638 clearHandles();
643 * Informs this <code>ObjectOutputStream</code> to write data
644 * according to the specified protocol. There are currently two
645 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
646 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
647 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
648 * since the JDK 1.2.
649 * <p>
650 * For an explanation of the differences between the two protocols
651 * see the Java Object Serialization Specification.
652 * </p>
654 * @param version the version to use.
656 * @throws IllegalArgumentException if <code>version</code> is not a valid
657 * protocol.
658 * @throws IllegalStateException if called after the first the first object
659 * was serialized.
660 * @throws IOException if an I/O error occurs.
662 * @see ObjectStreamConstants#PROTOCOL_VERSION_1
663 * @see ObjectStreamConstants#PROTOCOL_VERSION_2
665 * @since 1.2
667 public void useProtocolVersion(int version) throws IOException
669 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
670 throw new IllegalArgumentException("Invalid protocol version requested.");
672 if (nextOID != baseWireHandle)
673 throw new IllegalStateException("Protocol version cannot be changed "
674 + "after serialization started.");
676 protocolVersion = version;
680 * An empty hook that allows subclasses to write extra information
681 * about classes to the stream. This method is called the first
682 * time each class is seen, and after all of the standard
683 * information about the class has been written.
685 * @exception IOException Exception from underlying
686 * <code>OutputStream</code>.
688 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
690 protected void annotateClass(Class<?> cl) throws IOException
694 protected void annotateProxyClass(Class<?> cl) throws IOException
699 * Allows subclasses to replace objects that are written to the
700 * stream with other objects to be written in their place. This
701 * method is called the first time each object is encountered
702 * (modulo reseting of the stream).
704 * This method must be enabled before it will be called in the
705 * serialization process.
707 * @exception IOException Exception from underlying
708 * <code>OutputStream</code>.
710 * @see #enableReplaceObject(boolean)
712 protected Object replaceObject(Object obj) throws IOException
714 return obj;
719 * If <code>enable</code> is <code>true</code> and this object is
720 * trusted, then <code>replaceObject (Object)</code> will be called
721 * in subsequent calls to <code>writeObject (Object)</code>.
722 * Otherwise, <code>replaceObject (Object)</code> will not be called.
724 * @exception SecurityException This class is not trusted.
726 protected boolean enableReplaceObject(boolean enable)
727 throws SecurityException
729 if (enable)
731 SecurityManager sm = System.getSecurityManager();
732 if (sm != null)
733 sm.checkPermission(new SerializablePermission("enableSubstitution"));
736 boolean old_val = replacementEnabled;
737 replacementEnabled = enable;
738 return old_val;
743 * Writes stream magic and stream version information to the
744 * underlying stream.
746 * @exception IOException Exception from underlying
747 * <code>OutputStream</code>.
749 protected void writeStreamHeader() throws IOException
751 realOutput.writeShort(STREAM_MAGIC);
752 realOutput.writeShort(STREAM_VERSION);
756 * Protected constructor that allows subclasses to override
757 * serialization. This constructor should be called by subclasses
758 * that wish to override <code>writeObject (Object)</code>. This
759 * method does a security check <i>NOTE: currently not
760 * implemented</i>, then sets a flag that informs
761 * <code>writeObject (Object)</code> to call the subclasses
762 * <code>writeObjectOverride (Object)</code> method.
764 * @see #writeObjectOverride(Object)
766 protected ObjectOutputStream() throws IOException, SecurityException
768 SecurityManager sec_man = System.getSecurityManager ();
769 if (sec_man != null)
770 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
771 useSubclassMethod = true;
776 * This method allows subclasses to override the default
777 * serialization mechanism provided by
778 * <code>ObjectOutputStream</code>. To make this method be used for
779 * writing objects, subclasses must invoke the 0-argument
780 * constructor on this class from there constructor.
782 * @see #ObjectOutputStream()
784 * @exception NotActiveException Subclass has arranged for this
785 * method to be called, but did not implement this method.
787 protected void writeObjectOverride(Object obj) throws NotActiveException,
788 IOException
790 throw new NotActiveException
791 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
796 * @see DataOutputStream#write(int)
798 public void write (int data) throws IOException
800 if (writeDataAsBlocks)
802 if (blockDataCount == BUFFER_SIZE)
803 drain();
805 blockData[ blockDataCount++ ] = (byte)data;
807 else
808 realOutput.write(data);
813 * @see DataOutputStream#write(byte[])
815 public void write(byte[] b) throws IOException
817 write(b, 0, b.length);
822 * @see DataOutputStream#write(byte[],int,int)
824 public void write(byte[] b, int off, int len) throws IOException
826 if (writeDataAsBlocks)
828 if (len < 0)
829 throw new IndexOutOfBoundsException();
831 if (blockDataCount + len < BUFFER_SIZE)
833 System.arraycopy(b, off, blockData, blockDataCount, len);
834 blockDataCount += len;
836 else
838 drain();
839 writeBlockDataHeader(len);
840 realOutput.write(b, off, len);
843 else
844 realOutput.write(b, off, len);
849 * @see DataOutputStream#flush()
851 public void flush () throws IOException
853 drain();
854 realOutput.flush();
859 * Causes the block-data buffer to be written to the underlying
860 * stream, but does not flush underlying stream.
862 * @exception IOException Exception from underlying
863 * <code>OutputStream</code>.
865 protected void drain() throws IOException
867 if (blockDataCount == 0)
868 return;
870 if (writeDataAsBlocks)
871 writeBlockDataHeader(blockDataCount);
872 realOutput.write(blockData, 0, blockDataCount);
873 blockDataCount = 0;
878 * @see java.io.DataOutputStream#close ()
880 public void close() throws IOException
882 flush();
883 realOutput.close();
888 * @see java.io.DataOutputStream#writeBoolean (boolean)
890 public void writeBoolean(boolean data) throws IOException
892 blockDataOutput.writeBoolean(data);
897 * @see java.io.DataOutputStream#writeByte (int)
899 public void writeByte(int data) throws IOException
901 blockDataOutput.writeByte(data);
906 * @see java.io.DataOutputStream#writeShort (int)
908 public void writeShort (int data) throws IOException
910 blockDataOutput.writeShort(data);
915 * @see java.io.DataOutputStream#writeChar (int)
917 public void writeChar(int data) throws IOException
919 blockDataOutput.writeChar(data);
924 * @see java.io.DataOutputStream#writeInt (int)
926 public void writeInt(int data) throws IOException
928 blockDataOutput.writeInt(data);
933 * @see java.io.DataOutputStream#writeLong (long)
935 public void writeLong(long data) throws IOException
937 blockDataOutput.writeLong(data);
942 * @see java.io.DataOutputStream#writeFloat (float)
944 public void writeFloat(float data) throws IOException
946 blockDataOutput.writeFloat(data);
951 * @see java.io.DataOutputStream#writeDouble (double)
953 public void writeDouble(double data) throws IOException
955 blockDataOutput.writeDouble(data);
960 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
962 public void writeBytes(String data) throws IOException
964 blockDataOutput.writeBytes(data);
969 * @see java.io.DataOutputStream#writeChars (java.lang.String)
971 public void writeChars(String data) throws IOException
973 dataOutput.writeChars(data);
978 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
980 public void writeUTF(String data) throws IOException
982 dataOutput.writeUTF(data);
987 * This class allows a class to specify exactly which fields should
988 * be written, and what values should be written for these fields.
990 * XXX: finish up comments
992 public abstract static class PutField
994 public abstract void put (String name, boolean value);
995 public abstract void put (String name, byte value);
996 public abstract void put (String name, char value);
997 public abstract void put (String name, double value);
998 public abstract void put (String name, float value);
999 public abstract void put (String name, int value);
1000 public abstract void put (String name, long value);
1001 public abstract void put (String name, short value);
1002 public abstract void put (String name, Object value);
1005 * @deprecated
1007 public abstract void write (ObjectOutput out) throws IOException;
1010 public PutField putFields() throws IOException
1012 if (currentPutField != null)
1013 return currentPutField;
1015 currentPutField = new PutField()
1017 private byte[] prim_field_data
1018 = new byte[currentObjectStreamClass.primFieldSize];
1019 private Object[] objs
1020 = new Object[currentObjectStreamClass.objectFieldCount];
1022 private ObjectStreamField getField (String name)
1024 ObjectStreamField field
1025 = currentObjectStreamClass.getField(name);
1027 if (field == null)
1028 throw new IllegalArgumentException("no such serializable field " + name);
1030 return field;
1033 public void put(String name, boolean value)
1035 ObjectStreamField field = getField(name);
1037 checkType(field, 'Z');
1038 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
1041 public void put(String name, byte value)
1043 ObjectStreamField field = getField(name);
1045 checkType(field, 'B');
1046 prim_field_data[field.getOffset()] = value;
1049 public void put(String name, char value)
1051 ObjectStreamField field = getField(name);
1053 checkType(field, 'C');
1054 int off = field.getOffset();
1055 prim_field_data[off++] = (byte)(value >>> 8);
1056 prim_field_data[off] = (byte)value;
1059 public void put(String name, double value)
1061 ObjectStreamField field = getField (name);
1063 checkType(field, 'D');
1064 int off = field.getOffset();
1065 long l_value = Double.doubleToLongBits (value);
1066 prim_field_data[off++] = (byte)(l_value >>> 52);
1067 prim_field_data[off++] = (byte)(l_value >>> 48);
1068 prim_field_data[off++] = (byte)(l_value >>> 40);
1069 prim_field_data[off++] = (byte)(l_value >>> 32);
1070 prim_field_data[off++] = (byte)(l_value >>> 24);
1071 prim_field_data[off++] = (byte)(l_value >>> 16);
1072 prim_field_data[off++] = (byte)(l_value >>> 8);
1073 prim_field_data[off] = (byte)l_value;
1076 public void put(String name, float value)
1078 ObjectStreamField field = getField(name);
1080 checkType(field, 'F');
1081 int off = field.getOffset();
1082 int i_value = Float.floatToIntBits(value);
1083 prim_field_data[off++] = (byte)(i_value >>> 24);
1084 prim_field_data[off++] = (byte)(i_value >>> 16);
1085 prim_field_data[off++] = (byte)(i_value >>> 8);
1086 prim_field_data[off] = (byte)i_value;
1089 public void put(String name, int value)
1091 ObjectStreamField field = getField(name);
1092 checkType(field, 'I');
1093 int off = field.getOffset();
1094 prim_field_data[off++] = (byte)(value >>> 24);
1095 prim_field_data[off++] = (byte)(value >>> 16);
1096 prim_field_data[off++] = (byte)(value >>> 8);
1097 prim_field_data[off] = (byte)value;
1100 public void put(String name, long value)
1102 ObjectStreamField field = getField(name);
1103 checkType(field, 'J');
1104 int off = field.getOffset();
1105 prim_field_data[off++] = (byte)(value >>> 52);
1106 prim_field_data[off++] = (byte)(value >>> 48);
1107 prim_field_data[off++] = (byte)(value >>> 40);
1108 prim_field_data[off++] = (byte)(value >>> 32);
1109 prim_field_data[off++] = (byte)(value >>> 24);
1110 prim_field_data[off++] = (byte)(value >>> 16);
1111 prim_field_data[off++] = (byte)(value >>> 8);
1112 prim_field_data[off] = (byte)value;
1115 public void put(String name, short value)
1117 ObjectStreamField field = getField(name);
1118 checkType(field, 'S');
1119 int off = field.getOffset();
1120 prim_field_data[off++] = (byte)(value >>> 8);
1121 prim_field_data[off] = (byte)value;
1124 public void put(String name, Object value)
1126 ObjectStreamField field = getField(name);
1128 if (value != null &&
1129 ! field.getType().isAssignableFrom(value.getClass ()))
1130 throw new IllegalArgumentException("Class " + value.getClass() +
1131 " cannot be cast to " + field.getType());
1132 objs[field.getOffset()] = value;
1135 public void write(ObjectOutput out) throws IOException
1137 // Apparently Block data is not used with PutField as per
1138 // empirical evidence against JDK 1.2. Also see Mauve test
1139 // java.io.ObjectInputOutput.Test.GetPutField.
1140 boolean oldmode = setBlockDataMode(false);
1141 out.write(prim_field_data);
1142 for (int i = 0; i < objs.length; ++ i)
1143 out.writeObject(objs[i]);
1144 setBlockDataMode(oldmode);
1147 private void checkType(ObjectStreamField field, char type)
1148 throws IllegalArgumentException
1150 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1151 != type)
1152 throw new IllegalArgumentException();
1155 // end PutFieldImpl
1157 return currentPutField;
1161 public void writeFields() throws IOException
1163 if (currentPutField == null)
1164 throw new NotActiveException("writeFields can only be called after putFields has been called");
1166 markFieldsWritten();
1167 currentPutField.write(this);
1171 // write out the block-data buffer, picking the correct header
1172 // depending on the size of the buffer
1173 private void writeBlockDataHeader(int size) throws IOException
1175 if (size < 256)
1177 realOutput.writeByte(TC_BLOCKDATA);
1178 realOutput.write(size);
1180 else
1182 realOutput.writeByte(TC_BLOCKDATALONG);
1183 realOutput.writeInt(size);
1188 // lookup the handle for OBJ, return null if OBJ doesn't have a
1189 // handle yet
1190 private int findHandle(Object obj)
1192 return OIDLookupTable.get(obj);
1196 // assigns the next availible handle to OBJ
1197 private int assignNewHandle(Object obj)
1199 OIDLookupTable.put(obj, nextOID);
1200 return nextOID++;
1204 // resets mapping from objects to handles
1205 private void clearHandles()
1207 nextOID = baseWireHandle;
1208 OIDLookupTable.clear();
1212 // write out array size followed by each element of the array
1213 private void writeArraySizeAndElements(Object array, Class clazz)
1214 throws IOException
1216 int length = Array.getLength(array);
1218 if (clazz.isPrimitive())
1220 if (clazz == Boolean.TYPE)
1222 boolean[] cast_array = (boolean[])array;
1223 realOutput.writeInt (length);
1224 for (int i = 0; i < length; i++)
1225 realOutput.writeBoolean(cast_array[i]);
1226 return;
1228 if (clazz == Byte.TYPE)
1230 byte[] cast_array = (byte[])array;
1231 realOutput.writeInt(length);
1232 realOutput.write(cast_array, 0, length);
1233 return;
1235 if (clazz == Character.TYPE)
1237 char[] cast_array = (char[])array;
1238 realOutput.writeInt(length);
1239 for (int i = 0; i < length; i++)
1240 realOutput.writeChar(cast_array[i]);
1241 return;
1243 if (clazz == Double.TYPE)
1245 double[] cast_array = (double[])array;
1246 realOutput.writeInt(length);
1247 for (int i = 0; i < length; i++)
1248 realOutput.writeDouble(cast_array[i]);
1249 return;
1251 if (clazz == Float.TYPE)
1253 float[] cast_array = (float[])array;
1254 realOutput.writeInt(length);
1255 for (int i = 0; i < length; i++)
1256 realOutput.writeFloat(cast_array[i]);
1257 return;
1259 if (clazz == Integer.TYPE)
1261 int[] cast_array = (int[])array;
1262 realOutput.writeInt(length);
1263 for (int i = 0; i < length; i++)
1264 realOutput.writeInt(cast_array[i]);
1265 return;
1267 if (clazz == Long.TYPE)
1269 long[] cast_array = (long[])array;
1270 realOutput.writeInt (length);
1271 for (int i = 0; i < length; i++)
1272 realOutput.writeLong(cast_array[i]);
1273 return;
1275 if (clazz == Short.TYPE)
1277 short[] cast_array = (short[])array;
1278 realOutput.writeInt (length);
1279 for (int i = 0; i < length; i++)
1280 realOutput.writeShort(cast_array[i]);
1281 return;
1284 else
1286 Object[] cast_array = (Object[])array;
1287 realOutput.writeInt(length);
1288 for (int i = 0; i < length; i++)
1289 writeObject(cast_array[i]);
1294 /* GCJ LOCAL */
1295 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1296 // FIELDS are already supposed already to be in canonical order, but
1297 // under some circumstances (to do with Proxies) this isn't the
1298 // case, so we call ensureFieldsSet().
1299 private void writeFields(Object obj, ObjectStreamClass osc)
1300 throws IOException
1302 osc.ensureFieldsSet(osc.forClass());
1303 /* END GCJ LOCAL */
1305 ObjectStreamField[] fields = osc.fields;
1306 boolean oldmode = setBlockDataMode(false);
1310 writeFields(obj,fields);
1312 catch (IllegalArgumentException _)
1314 InvalidClassException e = new InvalidClassException
1315 ("writing fields of class " + osc.forClass().getName());
1316 e.initCause(_);
1317 throw e;
1319 catch (IOException e)
1321 throw e;
1323 catch (Exception _)
1325 IOException e = new IOException("Unexpected exception " + _);
1326 e.initCause(_);
1327 throw(e);
1330 setBlockDataMode(oldmode);
1335 * Helper function for writeFields(Object,ObjectStreamClass): write
1336 * fields from given fields array. Pass exception on.
1338 * @param obj the object to be written
1340 * @param fields the fields of obj to be written.
1342 private void writeFields(Object obj, ObjectStreamField[] fields)
1343 throws
1344 IllegalArgumentException, IllegalAccessException, IOException
1346 for (int i = 0; i < fields.length; i++)
1348 ObjectStreamField osf = fields[i];
1349 Field field = osf.field;
1351 if (DEBUG && dump)
1352 dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
1354 switch (osf.getTypeCode())
1356 case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
1357 case 'B': realOutput.writeByte (field.getByte (obj)); break;
1358 case 'S': realOutput.writeShort (field.getShort (obj)); break;
1359 case 'C': realOutput.writeChar (field.getChar (obj)); break;
1360 case 'I': realOutput.writeInt (field.getInt (obj)); break;
1361 case 'F': realOutput.writeFloat (field.getFloat (obj)); break;
1362 case 'J': realOutput.writeLong (field.getLong (obj)); break;
1363 case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
1364 case 'L':
1365 case '[': writeObject (field.get (obj)); break;
1366 default:
1367 throw new IOException("Unexpected type code " + osf.getTypeCode());
1373 // Toggles writing primitive data to block-data buffer.
1374 // Package-private to avoid a trampoline constructor.
1375 boolean setBlockDataMode(boolean on) throws IOException
1377 if (on == writeDataAsBlocks)
1378 return on;
1380 drain();
1381 boolean oldmode = writeDataAsBlocks;
1382 writeDataAsBlocks = on;
1384 if (on)
1385 dataOutput = blockDataOutput;
1386 else
1387 dataOutput = realOutput;
1389 return oldmode;
1393 private void callWriteMethod(Object obj, ObjectStreamClass osc)
1394 throws IOException
1396 currentPutField = null;
1399 Object args[] = {this};
1400 osc.writeObjectMethod.invoke(obj, args);
1402 catch (InvocationTargetException x)
1404 /* Rethrow if possible. */
1405 Throwable exception = x.getTargetException();
1406 if (exception instanceof RuntimeException)
1407 throw (RuntimeException) exception;
1408 if (exception instanceof IOException)
1409 throw (IOException) exception;
1411 IOException ioe
1412 = new IOException("Exception thrown from writeObject() on " +
1413 osc.forClass().getName() + ": " +
1414 exception.getClass().getName());
1415 ioe.initCause(exception);
1416 throw ioe;
1418 catch (Exception x)
1420 IOException ioe
1421 = new IOException("Failure invoking writeObject() on " +
1422 osc.forClass().getName() + ": " +
1423 x.getClass().getName());
1424 ioe.initCause(x);
1425 throw ioe;
1429 private void dumpElementln (String msg, Object obj)
1433 for (int i = 0; i < depth; i++)
1434 System.out.print (" ");
1435 System.out.print (Thread.currentThread() + ": ");
1436 System.out.print (msg);
1437 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
1438 System.out.print (obj.getClass());
1439 else
1440 System.out.print (obj);
1442 catch (Exception _)
1445 finally
1447 System.out.println ();
1451 private void dumpElementln (String msg)
1453 for (int i = 0; i < depth; i++)
1454 System.out.print (" ");
1455 System.out.print (Thread.currentThread() + ": ");
1456 System.out.println(msg);
1459 // this value comes from 1.2 spec, but is used in 1.1 as well
1460 private static final int BUFFER_SIZE = 1024;
1462 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1464 private DataOutputStream dataOutput;
1465 private boolean writeDataAsBlocks;
1466 private DataOutputStream realOutput;
1467 private DataOutputStream blockDataOutput;
1468 private byte[] blockData;
1469 private int blockDataCount;
1470 private Object currentObject;
1471 // Package-private to avoid a trampoline.
1472 ObjectStreamClass currentObjectStreamClass;
1473 private PutField currentPutField;
1474 private boolean fieldsAlreadyWritten;
1475 private boolean replacementEnabled;
1476 private boolean isSerializing;
1477 private int nextOID;
1478 private ObjectIdentityMap2Int OIDLookupTable;
1479 private int protocolVersion;
1480 private boolean useSubclassMethod;
1481 private SetAccessibleAction setAccessible = new SetAccessibleAction();
1483 // The nesting depth for debugging output
1484 private int depth = 0;
1486 // Set if we're generating debugging dumps
1487 private boolean dump = false;
1489 private static final boolean DEBUG = false;