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