OSDN Git Service

2007-04-25 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / io / ObjectOutputStream.java
1 /* ObjectOutputStream.java -- Class used to write serialized objects
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
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.
11
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.
16
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.
21
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.
26
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. */
38
39
40 package java.io;
41
42 import gnu.java.io.ObjectIdentityMap2Int;
43 import gnu.java.lang.reflect.TypeSignature;
44 import gnu.java.security.action.SetAccessibleAction;
45
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
51
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>.
56  *
57  * The data produced by an <code>ObjectOutputStream</code> can be read
58  * and reconstituted by an <code>ObjectInputStream</code>.
59  *
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.
64  *
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.
68  *
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.
73  *
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.
79  *
80  * Example usage:
81  * <pre>
82  * Hashtable map = new Hashtable ();
83  * map.put ("one", new Integer (1));
84  * map.put ("two", new Integer (2));
85  *
86  * ObjectOutputStream oos =
87  * new ObjectOutputStream (new FileOutputStream ("numbers"));
88  * oos.writeObject (map);
89  * oos.close ();
90  *
91  * ObjectInputStream ois =
92  * new ObjectInputStream (new FileInputStream ("numbers"));
93  * Hashtable newmap = (Hashtable)ois.readObject ();
94  *
95  * System.out.println (newmap);
96  * </pre>
97  *
98  * The default serialization can be overriden in two ways.
99  *
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.
106  *
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>.
112  *
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)
122  */
123 public class ObjectOutputStream extends OutputStream
124   implements ObjectOutput, ObjectStreamConstants
125 {
126   /**
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).
131    *
132    * @exception IOException Writing stream header to underlying
133    * stream cannot be completed.
134    *
135    * @see #writeStreamHeader()
136    */
137   public ObjectOutputStream (OutputStream out) throws IOException
138   {
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();
151
152     if (DEBUG)
153       {
154         String val = System.getProperty("gcj.dumpobjects");
155         if (val != null && !val.equals(""))
156           dump = true;
157       }
158   }
159
160   /**
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.
166    *
167    * This method can be overriden by a class by implementing
168    * <code>private void writeObject (ObjectOutputStream)</code>.
169    *
170    * If an exception is thrown from this method, the stream is left in
171    * an undefined state.
172    *
173    * @exception NotSerializableException An attempt was made to
174    * serialize an <code>Object</code> that is not serializable.
175    *
176    * @exception InvalidClassException Somebody tried to serialize
177    * an object which is wrongly formatted.
178    *
179    * @exception IOException Exception from underlying
180    * <code>OutputStream</code>.
181    */
182   public final void writeObject(Object obj) throws IOException
183   {
184     if (useSubclassMethod)
185       {
186         if (dump)
187           dumpElementln ("WRITE OVERRIDE: " + obj);
188           
189         writeObjectOverride(obj);
190         return;
191       }
192
193     if (dump)
194       dumpElementln ("WRITE: ", obj);
195     
196     depth += 2;    
197
198     boolean was_serializing = isSerializing;
199     boolean old_mode = setBlockDataMode(false);
200     try
201       {
202         isSerializing = true;
203         boolean replaceDone = false;
204         Object replacedObject = null;
205         
206         while (true)
207           {
208             if (obj == null)
209               {
210                 realOutput.writeByte(TC_NULL);
211                 break;
212               }
213
214             int handle = findHandle(obj);
215             if (handle >= 0)
216               {
217                 realOutput.writeByte(TC_REFERENCE);
218                 realOutput.writeInt(handle);
219                 break;
220               }
221
222             if (obj instanceof Class)
223               {
224                 Class cl = (Class)obj;
225                 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl);
226                 realOutput.writeByte(TC_CLASS);
227                 if (!osc.isProxyClass)
228                   {
229                     writeObject (osc);
230                   }
231                 else
232                   {System.err.println("1");
233                     realOutput.writeByte(TC_PROXYCLASSDESC);
234                     Class[] intfs = cl.getInterfaces();
235                     realOutput.writeInt(intfs.length);
236                     for (int i = 0; i < intfs.length; i++)
237                       realOutput.writeUTF(intfs[i].getName());
238                     
239                     boolean oldmode = setBlockDataMode(true);
240                     annotateProxyClass(cl);
241                     setBlockDataMode(oldmode);
242                     realOutput.writeByte(TC_ENDBLOCKDATA);
243                     
244                     writeObject(osc.getSuper());
245                   }
246                 assignNewHandle(obj);
247                 break;
248               }
249
250             if (obj instanceof ObjectStreamClass)
251               {
252                 writeClassDescriptor((ObjectStreamClass) obj);
253                 break;
254               }
255
256             Class clazz = obj.getClass();
257             ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz);
258             if (osc == null)
259               throw new NotSerializableException(clazz.getName());
260
261             if (osc.isEnum())
262               {
263                 /* TC_ENUM classDesc newHandle enumConstantName */
264                 realOutput.writeByte(TC_ENUM);
265                 writeObject(osc);
266                 assignNewHandle(obj);
267                 writeObject(((Enum) obj).name());
268                 break;
269               }
270
271             if ((replacementEnabled || obj instanceof Serializable)
272                 && ! replaceDone)
273               {
274                 replacedObject = obj;
275                 
276                 if (obj instanceof Serializable)
277                   {
278                     try
279                       {
280                         Method m = osc.writeReplaceMethod;
281                         if (m != null)
282                             obj = m.invoke(obj, new Object[0]);
283                       }
284                     catch (IllegalAccessException ignore)
285                       {
286                       }
287                     catch (InvocationTargetException ignore)
288                       {
289                       }
290                   }
291                 
292                 if (replacementEnabled)
293                   obj = replaceObject(obj);
294                 
295                 replaceDone = true;
296                 continue;
297               }
298
299             if (obj instanceof String)
300               {
301                 realOutput.writeByte(TC_STRING);
302                 assignNewHandle(obj);
303                 realOutput.writeUTF((String)obj);
304                 break;
305               }
306
307             if (clazz.isArray ())
308               {
309                 realOutput.writeByte(TC_ARRAY);
310                 writeObject(osc);
311                 assignNewHandle(obj);
312                 writeArraySizeAndElements(obj, clazz.getComponentType());
313                 break;
314               }
315             
316             realOutput.writeByte(TC_OBJECT);
317             writeObject(osc);
318
319             if (replaceDone)
320               assignNewHandle(replacedObject);
321             else
322               assignNewHandle(obj);
323
324             if (obj instanceof Externalizable)
325               {
326                 if (protocolVersion == PROTOCOL_VERSION_2)
327                   setBlockDataMode(true);
328                 
329                 ((Externalizable)obj).writeExternal(this);
330                 
331                 if (protocolVersion == PROTOCOL_VERSION_2)
332                   {
333                     setBlockDataMode(false);
334                     realOutput.writeByte(TC_ENDBLOCKDATA);
335                   }
336
337                 break;
338               }
339
340             if (obj instanceof Serializable)
341               {
342                 Object prevObject = this.currentObject;
343                 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
344                 currentObject = obj;
345                 ObjectStreamClass[] hierarchy = osc.hierarchy();
346                 
347                 for (int i = 0; i < hierarchy.length; i++)
348                   {
349                     currentObjectStreamClass = hierarchy[i];
350                     
351                     fieldsAlreadyWritten = false;
352                     if (currentObjectStreamClass.hasWriteMethod())
353                       {
354                         if (dump)
355                           dumpElementln ("WRITE METHOD CALLED FOR: ", obj);
356                         setBlockDataMode(true);
357                         callWriteMethod(obj, currentObjectStreamClass);
358                         setBlockDataMode(false);
359                         realOutput.writeByte(TC_ENDBLOCKDATA);
360                         if (dump)
361                           dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj);
362                       }
363                     else
364                       {
365                         if (dump)
366                           dumpElementln ("WRITE FIELDS CALLED FOR: ", obj);
367                         writeFields(obj, currentObjectStreamClass);
368                       }
369                   }
370
371                 this.currentObject = prevObject;
372                 this.currentObjectStreamClass = prevObjectStreamClass;
373                 currentPutField = null;
374                 break;
375               }
376
377             throw new NotSerializableException(clazz.getName()
378                                                + " in "
379                                                + obj.getClass());
380           } // end pseudo-loop
381       }
382     catch (ObjectStreamException ose)
383       {
384         // Rethrow these are fatal.
385         throw ose;
386       }
387     catch (IOException e)
388       {
389         realOutput.writeByte(TC_EXCEPTION);
390         reset(true);
391
392         setBlockDataMode(false);
393         try
394           {
395             if (DEBUG)
396               {
397                 e.printStackTrace(System.out);
398               }
399             writeObject(e);
400           }
401         catch (IOException ioe)
402           {
403             StreamCorruptedException ex = 
404               new StreamCorruptedException
405               (ioe + " thrown while exception was being written to stream.");
406             if (DEBUG)
407               {
408                 ex.printStackTrace(System.out);
409               }
410             throw ex;
411           }
412
413         reset (true);
414         
415       }
416     finally
417       {
418         isSerializing = was_serializing;
419         setBlockDataMode(old_mode);
420         depth -= 2;
421
422         if (dump)
423           dumpElementln ("END: ", obj);
424       }
425   }
426
427   protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException
428   {
429     if (osc.isProxyClass)
430       {
431         realOutput.writeByte(TC_PROXYCLASSDESC);
432         Class[] intfs = osc.forClass().getInterfaces();
433         realOutput.writeInt(intfs.length);
434         for (int i = 0; i < intfs.length; i++)
435           realOutput.writeUTF(intfs[i].getName());
436
437         assignNewHandle(osc);
438     
439         boolean oldmode = setBlockDataMode(true);
440         annotateProxyClass(osc.forClass());
441         setBlockDataMode(oldmode);
442         realOutput.writeByte(TC_ENDBLOCKDATA);
443       }
444     else
445       {
446         realOutput.writeByte(TC_CLASSDESC);
447         realOutput.writeUTF(osc.getName());
448         if (osc.isEnum())
449           realOutput.writeLong(0L);
450         else
451           realOutput.writeLong(osc.getSerialVersionUID());
452         assignNewHandle(osc);
453
454         int flags = osc.getFlags();
455
456         if (protocolVersion == PROTOCOL_VERSION_2
457             && osc.isExternalizable())
458         flags |= SC_BLOCK_DATA;
459
460         realOutput.writeByte(flags);
461
462         ObjectStreamField[] fields = osc.fields;
463
464         if (fields == ObjectStreamClass.INVALID_FIELDS)
465           throw new InvalidClassException
466                   (osc.getName(), "serialPersistentFields is invalid");
467
468         realOutput.writeShort(fields.length);
469
470         ObjectStreamField field;
471         for (int i = 0; i < fields.length; i++)
472           {
473             field = fields[i];
474             realOutput.writeByte(field.getTypeCode ());
475             realOutput.writeUTF(field.getName ());
476
477             if (! field.isPrimitive())
478               writeObject(field.getTypeString());
479           }
480
481         boolean oldmode = setBlockDataMode(true);
482         annotateClass(osc.forClass());
483         setBlockDataMode(oldmode);
484         realOutput.writeByte(TC_ENDBLOCKDATA);
485       }
486
487     if (osc.isSerializable() || osc.isExternalizable())
488       writeObject(osc.getSuper());
489     else
490       writeObject(null);
491   }
492   
493   /**
494    * Writes the current objects non-transient, non-static fields from
495    * the current class to the underlying output stream.
496    *
497    * This method is intended to be called from within a object's
498    * <code>private void writeObject (ObjectOutputStream)</code>
499    * method.
500    *
501    * @exception NotActiveException This method was called from a
502    * context other than from the current object's and current class's
503    * <code>private void writeObject (ObjectOutputStream)</code>
504    * method.
505    *
506    * @exception IOException Exception from underlying
507    * <code>OutputStream</code>.
508    */
509   public void defaultWriteObject()
510     throws IOException, NotActiveException
511   {
512     markFieldsWritten();
513     writeFields(currentObject, currentObjectStreamClass);
514   }
515
516
517   private void markFieldsWritten() throws IOException
518   {
519     if (currentObject == null || currentObjectStreamClass == null)
520       throw new NotActiveException
521         ("defaultWriteObject called by non-active class and/or object");
522
523     if (fieldsAlreadyWritten)
524       throw new IOException
525         ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
526
527     fieldsAlreadyWritten = true;
528   }
529
530   /**
531    * Resets stream to state equivalent to the state just after it was
532    * constructed.
533    *
534    * Causes all objects previously written to the stream to be
535    * forgotten.  A notification of this reset is also written to the
536    * underlying stream.
537    *
538    * @exception IOException Exception from underlying
539    * <code>OutputStream</code> or reset called while serialization is
540    * in progress.
541    */
542   public void reset() throws IOException
543   {
544     reset(false);
545   }
546
547
548   private void reset(boolean internal) throws IOException
549   {
550     if (!internal)
551       {
552         if (isSerializing)
553           throw new IOException("Reset called while serialization in progress");
554
555         realOutput.writeByte(TC_RESET);
556       }
557     
558     clearHandles();
559   }
560
561
562   /**
563    * Informs this <code>ObjectOutputStream</code> to write data
564    * according to the specified protocol.  There are currently two
565    * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
566    * and <code>PROTOCOL_VERSION_2</code>.  This implementation writes
567    * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
568    * since the JDK 1.2.
569    * <p>
570    * For an explanation of the differences between the two protocols
571    * see the Java Object Serialization Specification.
572    * </p>
573    * 
574    * @param version the version to use.
575    * 
576    * @throws IllegalArgumentException if <code>version</code> is not a valid 
577    * protocol.
578    * @throws IllegalStateException if called after the first the first object
579    * was serialized.
580    * @throws IOException if an I/O error occurs.
581    * 
582    * @see ObjectStreamConstants#PROTOCOL_VERSION_1
583    * @see ObjectStreamConstants#PROTOCOL_VERSION_2
584    * 
585    * @since 1.2
586    */
587   public void useProtocolVersion(int version) throws IOException
588   {
589     if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
590       throw new IllegalArgumentException("Invalid protocol version requested.");
591     
592     if (nextOID != baseWireHandle)
593       throw new IllegalStateException("Protocol version cannot be changed " 
594                                       + "after serialization started.");
595     
596     protocolVersion = version;
597   }
598
599   /**
600    * An empty hook that allows subclasses to write extra information
601    * about classes to the stream.  This method is called the first
602    * time each class is seen, and after all of the standard
603    * information about the class has been written.
604    *
605    * @exception IOException Exception from underlying
606    * <code>OutputStream</code>.
607    *
608    * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
609    */
610   protected void annotateClass(Class<?> cl) throws IOException
611   {
612   }
613
614   protected void annotateProxyClass(Class<?> cl) throws IOException
615   {
616   }
617
618   /**
619    * Allows subclasses to replace objects that are written to the
620    * stream with other objects to be written in their place.  This
621    * method is called the first time each object is encountered
622    * (modulo reseting of the stream).
623    *
624    * This method must be enabled before it will be called in the
625    * serialization process.
626    *
627    * @exception IOException Exception from underlying
628    * <code>OutputStream</code>.
629    *
630    * @see #enableReplaceObject(boolean)
631    */
632   protected Object replaceObject(Object obj) throws IOException
633   {
634     return obj;
635   }
636
637
638   /**
639    * If <code>enable</code> is <code>true</code> and this object is
640    * trusted, then <code>replaceObject (Object)</code> will be called
641    * in subsequent calls to <code>writeObject (Object)</code>.
642    * Otherwise, <code>replaceObject (Object)</code> will not be called.
643    *
644    * @exception SecurityException This class is not trusted.
645    */
646   protected boolean enableReplaceObject(boolean enable)
647     throws SecurityException
648   {
649     if (enable)
650       {
651         SecurityManager sm = System.getSecurityManager();
652         if (sm != null)
653           sm.checkPermission(new SerializablePermission("enableSubstitution"));
654       }
655
656     boolean old_val = replacementEnabled;
657     replacementEnabled = enable;
658     return old_val;
659   }
660
661
662   /**
663    * Writes stream magic and stream version information to the
664    * underlying stream.
665    *
666    * @exception IOException Exception from underlying
667    * <code>OutputStream</code>.
668    */
669   protected void writeStreamHeader() throws IOException
670   {
671     realOutput.writeShort(STREAM_MAGIC);
672     realOutput.writeShort(STREAM_VERSION);
673   }
674
675   /**
676    * Protected constructor that allows subclasses to override
677    * serialization.  This constructor should be called by subclasses
678    * that wish to override <code>writeObject (Object)</code>.  This
679    * method does a security check <i>NOTE: currently not
680    * implemented</i>, then sets a flag that informs
681    * <code>writeObject (Object)</code> to call the subclasses
682    * <code>writeObjectOverride (Object)</code> method.
683    *
684    * @see #writeObjectOverride(Object)
685    */
686   protected ObjectOutputStream() throws IOException, SecurityException
687   {
688     SecurityManager sec_man = System.getSecurityManager ();
689     if (sec_man != null)
690       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
691     useSubclassMethod = true;
692   }
693
694
695   /**
696    * This method allows subclasses to override the default
697    * serialization mechanism provided by
698    * <code>ObjectOutputStream</code>.  To make this method be used for
699    * writing objects, subclasses must invoke the 0-argument
700    * constructor on this class from there constructor.
701    *
702    * @see #ObjectOutputStream()
703    *
704    * @exception NotActiveException Subclass has arranged for this
705    * method to be called, but did not implement this method.
706    */
707   protected void writeObjectOverride(Object obj) throws NotActiveException,
708     IOException
709   {
710     throw new NotActiveException
711       ("Subclass of ObjectOutputStream must implement writeObjectOverride");
712   }
713
714
715   /**
716    * @see DataOutputStream#write(int)
717    */
718   public void write (int data) throws IOException
719   {
720     if (writeDataAsBlocks)
721       {
722         if (blockDataCount == BUFFER_SIZE)
723           drain();
724
725         blockData[ blockDataCount++ ] = (byte)data;
726       }
727     else
728       realOutput.write(data);
729   }
730
731
732   /**
733    * @see DataOutputStream#write(byte[])
734    */
735   public void write(byte[] b) throws IOException
736   {
737     write(b, 0, b.length);
738   }
739
740
741   /**
742    * @see DataOutputStream#write(byte[],int,int)
743    */
744   public void write(byte[] b, int off, int len) throws IOException
745   {
746     if (writeDataAsBlocks)
747       {
748         if (len < 0)
749           throw new IndexOutOfBoundsException();
750
751         if (blockDataCount + len < BUFFER_SIZE)
752           {
753             System.arraycopy(b, off, blockData, blockDataCount, len);
754             blockDataCount += len;
755           }
756         else
757           {
758             drain();
759             writeBlockDataHeader(len);
760             realOutput.write(b, off, len);
761           }
762       }
763     else
764       realOutput.write(b, off, len);
765   }
766
767
768   /**
769    * @see DataOutputStream#flush()
770    */
771   public void flush () throws IOException
772   {
773     drain();
774     realOutput.flush();
775   }
776
777
778   /**
779    * Causes the block-data buffer to be written to the underlying
780    * stream, but does not flush underlying stream.
781    *
782    * @exception IOException Exception from underlying
783    * <code>OutputStream</code>.
784    */
785   protected void drain() throws IOException
786   {
787     if (blockDataCount == 0)
788       return;
789
790     if (writeDataAsBlocks)
791       writeBlockDataHeader(blockDataCount);
792     realOutput.write(blockData, 0, blockDataCount);
793     blockDataCount = 0;
794   }
795
796
797   /**
798    * @see java.io.DataOutputStream#close ()
799    */
800   public void close() throws IOException
801   {
802     flush();
803     realOutput.close();
804   }
805
806
807   /**
808    * @see java.io.DataOutputStream#writeBoolean (boolean)
809    */
810   public void writeBoolean(boolean data) throws IOException
811   {
812     blockDataOutput.writeBoolean(data);
813   }
814
815
816   /**
817    * @see java.io.DataOutputStream#writeByte (int)
818    */
819   public void writeByte(int data) throws IOException
820   {
821     blockDataOutput.writeByte(data);
822   }
823
824
825   /**
826    * @see java.io.DataOutputStream#writeShort (int)
827    */
828   public void writeShort (int data) throws IOException
829   {
830     blockDataOutput.writeShort(data);
831   }
832
833
834   /**
835    * @see java.io.DataOutputStream#writeChar (int)
836    */
837   public void writeChar(int data) throws IOException
838   {
839     blockDataOutput.writeChar(data);
840   }
841
842
843   /**
844    * @see java.io.DataOutputStream#writeInt (int)
845    */
846   public void writeInt(int data) throws IOException
847   {
848     blockDataOutput.writeInt(data);
849   }
850
851
852   /**
853    * @see java.io.DataOutputStream#writeLong (long)
854    */
855   public void writeLong(long data) throws IOException
856   {
857     blockDataOutput.writeLong(data);
858   }
859
860
861   /**
862    * @see java.io.DataOutputStream#writeFloat (float)
863    */
864   public void writeFloat(float data) throws IOException
865   {
866     blockDataOutput.writeFloat(data);
867   }
868
869
870   /**
871    * @see java.io.DataOutputStream#writeDouble (double)
872    */
873   public void writeDouble(double data) throws IOException
874   {
875     blockDataOutput.writeDouble(data);
876   }
877
878
879   /**
880    * @see java.io.DataOutputStream#writeBytes (java.lang.String)
881    */
882   public void writeBytes(String data) throws IOException
883   {
884     blockDataOutput.writeBytes(data);
885   }
886
887
888   /**
889    * @see java.io.DataOutputStream#writeChars (java.lang.String)
890    */
891   public void writeChars(String data) throws IOException
892   {
893     dataOutput.writeChars(data);
894   }
895
896
897   /**
898    * @see java.io.DataOutputStream#writeUTF (java.lang.String)
899    */
900   public void writeUTF(String data) throws IOException
901   {
902     dataOutput.writeUTF(data);
903   }
904
905
906   /**
907    * This class allows a class to specify exactly which fields should
908    * be written, and what values should be written for these fields.
909    *
910    * XXX: finish up comments
911    */
912   public abstract static class PutField
913   {
914     public abstract void put (String name, boolean value);
915     public abstract void put (String name, byte value);
916     public abstract void put (String name, char value);
917     public abstract void put (String name, double value);
918     public abstract void put (String name, float value);
919     public abstract void put (String name, int value);
920     public abstract void put (String name, long value);
921     public abstract void put (String name, short value);
922     public abstract void put (String name, Object value);
923
924     /**
925      * @deprecated
926      */
927     public abstract void write (ObjectOutput out) throws IOException;
928   }
929
930   public PutField putFields() throws IOException
931   {
932     if (currentPutField != null)
933       return currentPutField;
934
935     currentPutField = new PutField()
936       {
937         private byte[] prim_field_data
938           = new byte[currentObjectStreamClass.primFieldSize];
939         private Object[] objs
940           = new Object[currentObjectStreamClass.objectFieldCount];
941
942         private ObjectStreamField getField (String name)
943         {
944           ObjectStreamField field
945             = currentObjectStreamClass.getField(name);
946           
947           if (field == null)
948             throw new IllegalArgumentException("no such serializable field " + name);
949           
950           return field;
951         }
952         
953         public void put(String name, boolean value)
954         {
955           ObjectStreamField field = getField(name);
956
957           checkType(field, 'Z');
958           prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
959         }
960
961         public void put(String name, byte value)
962         {
963           ObjectStreamField field = getField(name);
964
965           checkType(field, 'B');
966           prim_field_data[field.getOffset()] = value;
967         }
968
969         public void put(String name, char value)
970         {
971           ObjectStreamField field = getField(name);
972
973           checkType(field, 'C');
974           int off = field.getOffset();
975           prim_field_data[off++] = (byte)(value >>> 8);
976           prim_field_data[off] = (byte)value;
977         }
978
979         public void put(String name, double value)
980         {
981           ObjectStreamField field = getField (name);
982
983           checkType(field, 'D');
984           int off = field.getOffset();
985           long l_value = Double.doubleToLongBits (value);
986           prim_field_data[off++] = (byte)(l_value >>> 52);
987           prim_field_data[off++] = (byte)(l_value >>> 48);
988           prim_field_data[off++] = (byte)(l_value >>> 40);
989           prim_field_data[off++] = (byte)(l_value >>> 32);
990           prim_field_data[off++] = (byte)(l_value >>> 24);
991           prim_field_data[off++] = (byte)(l_value >>> 16);
992           prim_field_data[off++] = (byte)(l_value >>> 8);
993           prim_field_data[off] = (byte)l_value;
994         }
995
996         public void put(String name, float value)
997         {
998           ObjectStreamField field = getField(name);
999
1000           checkType(field, 'F');
1001           int off = field.getOffset();
1002           int i_value = Float.floatToIntBits(value);
1003           prim_field_data[off++] = (byte)(i_value >>> 24);
1004           prim_field_data[off++] = (byte)(i_value >>> 16);
1005           prim_field_data[off++] = (byte)(i_value >>> 8);
1006           prim_field_data[off] = (byte)i_value;
1007         }
1008
1009         public void put(String name, int value)
1010         {
1011           ObjectStreamField field = getField(name);
1012           checkType(field, 'I');
1013           int off = field.getOffset();
1014           prim_field_data[off++] = (byte)(value >>> 24);
1015           prim_field_data[off++] = (byte)(value >>> 16);
1016           prim_field_data[off++] = (byte)(value >>> 8);
1017           prim_field_data[off] = (byte)value;
1018         }
1019
1020         public void put(String name, long value)
1021         {
1022           ObjectStreamField field = getField(name);
1023           checkType(field, 'J');
1024           int off = field.getOffset();
1025           prim_field_data[off++] = (byte)(value >>> 52);
1026           prim_field_data[off++] = (byte)(value >>> 48);
1027           prim_field_data[off++] = (byte)(value >>> 40);
1028           prim_field_data[off++] = (byte)(value >>> 32);
1029           prim_field_data[off++] = (byte)(value >>> 24);
1030           prim_field_data[off++] = (byte)(value >>> 16);
1031           prim_field_data[off++] = (byte)(value >>> 8);
1032           prim_field_data[off] = (byte)value;
1033         }
1034
1035         public void put(String name, short value)
1036         {
1037           ObjectStreamField field = getField(name);
1038           checkType(field, 'S');
1039           int off = field.getOffset();
1040           prim_field_data[off++] = (byte)(value >>> 8);
1041           prim_field_data[off] = (byte)value;
1042         }
1043
1044         public void put(String name, Object value)
1045         {
1046           ObjectStreamField field = getField(name);
1047
1048           if (value != null &&
1049               ! field.getType().isAssignableFrom(value.getClass ()))        
1050             throw new IllegalArgumentException("Class " + value.getClass() +
1051                                                " cannot be cast to " + field.getType());
1052           objs[field.getOffset()] = value;
1053         }
1054
1055         public void write(ObjectOutput out) throws IOException
1056         {
1057           // Apparently Block data is not used with PutField as per
1058           // empirical evidence against JDK 1.2.  Also see Mauve test
1059           // java.io.ObjectInputOutput.Test.GetPutField.
1060           boolean oldmode = setBlockDataMode(false);
1061           out.write(prim_field_data);
1062           for (int i = 0; i < objs.length; ++ i)
1063             out.writeObject(objs[i]);
1064           setBlockDataMode(oldmode);
1065         }
1066
1067         private void checkType(ObjectStreamField field, char type)
1068           throws IllegalArgumentException
1069         {
1070           if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1071               != type)
1072             throw new IllegalArgumentException();
1073         }
1074       };
1075     // end PutFieldImpl
1076
1077     return currentPutField;
1078   }
1079
1080
1081   public void writeFields() throws IOException
1082   {
1083     if (currentPutField == null)
1084       throw new NotActiveException("writeFields can only be called after putFields has been called");
1085
1086     markFieldsWritten();
1087     currentPutField.write(this);
1088   }
1089
1090
1091   // write out the block-data buffer, picking the correct header
1092   // depending on the size of the buffer
1093   private void writeBlockDataHeader(int size) throws IOException
1094   {
1095     if (size < 256)
1096       {
1097         realOutput.writeByte(TC_BLOCKDATA);
1098         realOutput.write(size);
1099       }
1100     else
1101       {
1102         realOutput.writeByte(TC_BLOCKDATALONG);
1103         realOutput.writeInt(size);
1104       }
1105   }
1106
1107
1108   // lookup the handle for OBJ, return null if OBJ doesn't have a
1109   // handle yet
1110   private int findHandle(Object obj)
1111   {
1112     return OIDLookupTable.get(obj);
1113   }
1114
1115
1116   // assigns the next availible handle to OBJ
1117   private int assignNewHandle(Object obj)
1118   {
1119     OIDLookupTable.put(obj, nextOID);
1120     return nextOID++;
1121   }
1122
1123
1124   // resets mapping from objects to handles
1125   private void clearHandles()
1126   {
1127     nextOID = baseWireHandle;
1128     OIDLookupTable.clear();
1129   }
1130
1131
1132   // write out array size followed by each element of the array
1133   private void writeArraySizeAndElements(Object array, Class clazz)
1134     throws IOException
1135   {
1136     int length = Array.getLength(array);
1137
1138     if (clazz.isPrimitive())
1139       {
1140         if (clazz == Boolean.TYPE)
1141           {
1142             boolean[] cast_array = (boolean[])array;
1143             realOutput.writeInt (length);
1144             for (int i = 0; i < length; i++)
1145               realOutput.writeBoolean(cast_array[i]);
1146             return;
1147           }
1148         if (clazz == Byte.TYPE)
1149           {
1150             byte[] cast_array = (byte[])array;
1151             realOutput.writeInt(length);
1152             realOutput.write(cast_array, 0, length);
1153             return;
1154           }
1155         if (clazz == Character.TYPE)
1156           {
1157             char[] cast_array = (char[])array;
1158             realOutput.writeInt(length);
1159             for (int i = 0; i < length; i++)
1160               realOutput.writeChar(cast_array[i]);
1161             return;
1162           }
1163         if (clazz == Double.TYPE)
1164           {
1165             double[] cast_array = (double[])array;
1166             realOutput.writeInt(length);
1167             for (int i = 0; i < length; i++)
1168               realOutput.writeDouble(cast_array[i]);
1169             return;
1170           }
1171         if (clazz == Float.TYPE)
1172           {
1173             float[] cast_array = (float[])array;
1174             realOutput.writeInt(length);
1175             for (int i = 0; i < length; i++)
1176               realOutput.writeFloat(cast_array[i]);
1177             return;
1178           }
1179         if (clazz == Integer.TYPE)
1180           {
1181             int[] cast_array = (int[])array;
1182             realOutput.writeInt(length);
1183             for (int i = 0; i < length; i++)
1184               realOutput.writeInt(cast_array[i]);
1185             return;
1186           }
1187         if (clazz == Long.TYPE)
1188           {
1189             long[] cast_array = (long[])array;
1190             realOutput.writeInt (length);
1191             for (int i = 0; i < length; i++)
1192               realOutput.writeLong(cast_array[i]);
1193             return;
1194           }
1195         if (clazz == Short.TYPE)
1196           {
1197             short[] cast_array = (short[])array;
1198             realOutput.writeInt (length);
1199             for (int i = 0; i < length; i++)
1200               realOutput.writeShort(cast_array[i]);
1201             return;
1202           }
1203       }
1204     else
1205       {
1206         Object[] cast_array = (Object[])array;
1207         realOutput.writeInt(length);
1208         for (int i = 0; i < length; i++)
1209           writeObject(cast_array[i]);
1210       }
1211   }
1212
1213
1214 /* GCJ LOCAL */
1215   // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1216   // FIELDS are already supposed already to be in canonical order, but
1217   // under some circumstances (to do with Proxies) this isn't the
1218   // case, so we call ensureFieldsSet().
1219   private void writeFields(Object obj, ObjectStreamClass osc)
1220     throws IOException
1221   {
1222     osc.ensureFieldsSet(osc.forClass());
1223 /* END GCJ LOCAL */
1224
1225     ObjectStreamField[] fields = osc.fields;
1226     boolean oldmode = setBlockDataMode(false);
1227
1228     try
1229       {
1230         writeFields(obj,fields);
1231       }
1232     catch (IllegalArgumentException _)
1233       {
1234         InvalidClassException e = new InvalidClassException
1235           ("writing fields of class " + osc.forClass().getName());
1236         e.initCause(_);
1237         throw e;
1238       }
1239     catch (IOException e)
1240       {
1241         throw e;
1242       }
1243     catch (Exception _)
1244       {
1245         IOException e = new IOException("Unexpected exception " + _);
1246         e.initCause(_);
1247         throw(e);
1248       }    
1249
1250     setBlockDataMode(oldmode);
1251   }
1252         
1253
1254   /**
1255    * Helper function for writeFields(Object,ObjectStreamClass): write
1256    * fields from given fields array.  Pass exception on.
1257    *
1258    * @param obj the object to be written
1259    *
1260    * @param fields the fields of obj to be written.
1261    */
1262   private void writeFields(Object obj, ObjectStreamField[] fields)
1263     throws
1264       IllegalArgumentException, IllegalAccessException, IOException
1265   {
1266     for (int i = 0; i < fields.length; i++)
1267       {
1268         ObjectStreamField osf = fields[i];
1269         Field field = osf.field;
1270         
1271         if (DEBUG && dump)
1272           dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType());
1273         
1274         switch (osf.getTypeCode())
1275           {
1276           case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break;
1277           case 'B': realOutput.writeByte   (field.getByte   (obj)); break;
1278           case 'S': realOutput.writeShort  (field.getShort  (obj)); break;
1279           case 'C': realOutput.writeChar   (field.getChar   (obj)); break;
1280           case 'I': realOutput.writeInt    (field.getInt    (obj)); break;
1281           case 'F': realOutput.writeFloat  (field.getFloat  (obj)); break;
1282           case 'J': realOutput.writeLong   (field.getLong   (obj)); break;
1283           case 'D': realOutput.writeDouble (field.getDouble (obj)); break;
1284           case 'L': 
1285           case '[':            writeObject (field.get       (obj)); break;
1286           default: 
1287             throw new IOException("Unexpected type code " + osf.getTypeCode());
1288           }
1289       }
1290   }
1291
1292
1293   // Toggles writing primitive data to block-data buffer.
1294   // Package-private to avoid a trampoline constructor.
1295   boolean setBlockDataMode(boolean on) throws IOException
1296   {
1297     if (on == writeDataAsBlocks)
1298       return on;
1299
1300     drain();
1301     boolean oldmode = writeDataAsBlocks;
1302     writeDataAsBlocks = on;
1303
1304     if (on)
1305       dataOutput = blockDataOutput;
1306     else
1307       dataOutput = realOutput;
1308
1309     return oldmode;
1310   }
1311
1312
1313   private void callWriteMethod(Object obj, ObjectStreamClass osc)
1314     throws IOException
1315   {
1316     currentPutField = null;
1317     try
1318       {
1319         Object args[] = {this};
1320         osc.writeObjectMethod.invoke(obj, args);
1321       }
1322     catch (InvocationTargetException x)
1323       {
1324         /* Rethrow if possible. */
1325         Throwable exception = x.getTargetException();
1326         if (exception instanceof RuntimeException)
1327           throw (RuntimeException) exception;
1328         if (exception instanceof IOException)
1329           throw (IOException) exception;
1330
1331         IOException ioe
1332           = new IOException("Exception thrown from writeObject() on " +
1333                             osc.forClass().getName() + ": " +
1334                             exception.getClass().getName());
1335         ioe.initCause(exception);
1336         throw ioe;
1337       }
1338     catch (Exception x)
1339       {
1340         IOException ioe
1341           = new IOException("Failure invoking writeObject() on " +
1342                             osc.forClass().getName() + ": " +
1343                             x.getClass().getName());
1344         ioe.initCause(x);
1345         throw ioe;
1346       }
1347   }
1348
1349   private void dumpElementln (String msg, Object obj)
1350   {
1351     try
1352       {
1353         for (int i = 0; i < depth; i++)
1354           System.out.print (" ");
1355         System.out.print (Thread.currentThread() + ": ");
1356         System.out.print (msg);
1357         if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
1358           System.out.print (obj.getClass());
1359         else
1360           System.out.print (obj);
1361       }
1362     catch (Exception _)
1363       {
1364       }
1365     finally
1366       {
1367         System.out.println ();
1368       }
1369   }
1370
1371   private void dumpElementln (String msg)
1372   {
1373     for (int i = 0; i < depth; i++)
1374       System.out.print (" ");
1375     System.out.print (Thread.currentThread() + ": ");
1376     System.out.println(msg);
1377   }
1378
1379   // this value comes from 1.2 spec, but is used in 1.1 as well
1380   private static final int BUFFER_SIZE = 1024;
1381
1382   private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1383
1384   private DataOutputStream dataOutput;
1385   private boolean writeDataAsBlocks;
1386   private DataOutputStream realOutput;
1387   private DataOutputStream blockDataOutput;
1388   private byte[] blockData;
1389   private int blockDataCount;
1390   private Object currentObject;
1391   // Package-private to avoid a trampoline.
1392   ObjectStreamClass currentObjectStreamClass;
1393   private PutField currentPutField;
1394   private boolean fieldsAlreadyWritten;
1395   private boolean replacementEnabled;
1396   private boolean isSerializing;
1397   private int nextOID;
1398   private ObjectIdentityMap2Int OIDLookupTable;
1399   private int protocolVersion;
1400   private boolean useSubclassMethod;
1401   private SetAccessibleAction setAccessible = new SetAccessibleAction();
1402
1403   // The nesting depth for debugging output
1404   private int depth = 0;
1405
1406   // Set if we're generating debugging dumps
1407   private boolean dump = false;
1408
1409   private static final boolean DEBUG = false;
1410 }