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