home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / main.bin / ObjectOutputStream.java < prev    next >
Text File  |  1998-01-23  |  41KB  |  1,213 lines

  1. /*
  2.  * @(#)ObjectOutputStream.java    1.30 97/10/01
  3.  * 
  4.  * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * CopyrightVersion 1.1_beta
  20.  */
  21.  
  22. package java.io;
  23.  
  24. import java.util.Stack;
  25.  
  26. /**
  27.  * An ObjectOutputStream writes primitive data types and graphs of
  28.  * Java objects to an OutputStream.  The objects can be read
  29.  * (reconstituted) using an ObjectInputStream.
  30.  * Persistent storage of objects can be accomplished by using a file for
  31.  * the stream.
  32.  * If the stream is a network socket stream, the objects can be reconsituted
  33.  * on another host or in another process. <p>
  34.  *
  35.  * Only objects that support the java.io.Serializable interface can be
  36.  * written to streams.
  37.  *
  38.  * The class of each serializable object is encoded including the class
  39.  * name and signature of the class, the values of the
  40.  * object's fields and arrays, and the closure of any other objects
  41.  * referenced from the initial objects. <p>
  42.  * 
  43.  * The method <STRONG>writeObject</STRONG> is used to write an object
  44.  * to the stream.  Any object, including Strings and arrays, is
  45.  * written with writeObject. Multiple objects or primitives can be
  46.  * written to the stream.  The objects must be read back from the
  47.  * corresponding ObjectInputstream with the same types and in the same
  48.  * order as they were written.<p>
  49.  *
  50.  * Primitive data types can also be written to the stream using the
  51.  * appropriate methods from DataOutput. Strings can also be written
  52.  * using the writeUTF method.<p>
  53.  *
  54.  * The default serialization mechanism for an object writes the class
  55.  * of the object, the class signature, and the values of all
  56.  * non-transient and non-static fields.  References to other objects
  57.  * (except in transient or static fields) cause those objects to be
  58.  * written also. Multiple references to a single object are encoded
  59.  * using a reference sharing mechanism so that graphs of objects can
  60.  * be restored to the same shape as when the original was written. <p>
  61.  *
  62.  * For example to write an object that can be read by the example in ObjectInputStream: <br>
  63.  * <PRE>
  64.  *    FileOutputStream ostream = new FileOutputStream("t.tmp");
  65.  *    ObjectOutputStream p = new ObjectOutputStream(ostream);
  66.  *
  67.  *    p.writeInt(12345);
  68.  *    p.writeObject("Today");
  69.  *    p.writeObject(new Date());
  70.  *
  71.  *    p.flush();
  72.  *    ostream.close();
  73.  *
  74.  * </PRE>
  75.  *
  76.  * Classes that require special handling during the serialization and deserialization
  77.  * process must implement special methods with these exact signatures: <p>
  78.  *
  79.  * <PRE>
  80.  * private void readObject(java.io.ObjectInputStream stream)
  81.  *     throws IOException, ClassNotFoundException; 
  82.  * private void writeObject(java.io.ObjectOutputStream stream)
  83.  *     throws IOException
  84.  * </PRE><p>
  85.  * The writeObject method is responsible for writing the state of
  86.  * the object for its particular class so that the corresponding
  87.  * readObject method can restore it.
  88.  * The method does not need to concern itself with the
  89.  * state belonging to the object's superclasses or subclasses.
  90.  * State is saved by writing the individual fields to the ObjectOutputStream
  91.  * using the writeObject method or by using the methods for
  92.  * primitive data types supported by DataOutput. <p>
  93.  *
  94.  * Serialization does not write out the fields of any object that does
  95.  * not implement the java.io.Serializable interface.  Subclasses of
  96.  * Objects that are not serializable can be serializable. In this case
  97.  * the non-serializable class must have a no-arg constructor to allow
  98.  * its fields to be initialized.  In this case it is the
  99.  * responsibility of the subclass to save and restore the state of the
  100.  * non-serializable class. It is frequently the case that the fields
  101.  * of that class are accessible (public, package, or protected) or
  102.  * that there are get and set methods that can be used to restore the
  103.  * state. <p>
  104.  *
  105.  * Serialization of an object can be prevented by implementing writeObject
  106.  * and readObject methods that throw the NotSerializableException.
  107.  * The exception will be caught by the ObjectOutputStream and abort the
  108.  * serialization process.
  109.  *
  110.  * Implementing the Externalizable interface allows the object to
  111.  * assume complete control over the contents and format of the object's
  112.  * serialized form.  The methods of the Externalizable interface,
  113.  * writeExternal and readExternal, are called to save and restore the
  114.  * objects state.  When implemented by a class they can write and read
  115.  * their own state using all of the methods of ObjectOutput and
  116.  * ObjectInput.  It is the responsibility of the objects to handle any
  117.  * versioning that occurs.
  118.  *
  119.  * @author    Roger Riggs
  120.  * @version     1.30, 10/01/97
  121.  * @see java.io.DataOutput
  122.  * @see java.io.ObjectInputStream
  123.  * @see java.io.Serializable
  124.  * @see java.io.Externalizable
  125.  * @since       JDK1.1
  126.  */
  127. public class ObjectOutputStream
  128.     extends OutputStream
  129.     implements ObjectOutput, ObjectStreamConstants
  130.             
  131.     /** 
  132.      * Creates an ObjectOutputStream that writes to the specified OutputStream.
  133.      * The stream header is written to the stream. The caller may want to call
  134.      * flush immediately so that the corresponding ObjectInputStream can read
  135.      * the header immediately.
  136.      * @exception IOException Any exception thrown by the underlying OutputStream.
  137.      * @since     JDK1.1
  138.      */
  139.     public ObjectOutputStream(OutputStream out) throws IOException {
  140.     this.out = out;
  141.     dos = new DataOutputStream(this);
  142.     buf = new byte[1024];    // allocate buffer
  143.     writeStreamHeader();
  144.     resetStream();
  145.     }
  146.  
  147.     /**
  148.      * Write the specified object to the ObjectOutputStream.
  149.      * The class of the object, the signature of the class, and the values
  150.      * of the non-transient and non-static fields of the class and all
  151.      * of its supertypes are written.  Default serialization for a class can be
  152.      * overridden using the writeObject and the readObject methods. 
  153.      * Objects referenced by this object are written transitively so
  154.      * that a complete equivalent graph of objects can be
  155.      * reconstructed by an ObjectInputStream.  <p>
  156.      *
  157.      * Exceptions are thrown for
  158.      * problems with the OutputStream and for classes that should not be
  159.      * serialized.  All exceptions are fatal to the OutputStream, which
  160.      * is left in an indeterminate state, and it is up to the caller
  161.      * to ignore or recover the stream state.
  162.      * @exception InvalidClassException Something is wrong with a class used by
  163.      *       serialization.
  164.      * @exception NotSerializableException Some object to be serialized does not
  165.      *      implement the java.io.Serializable interface.
  166.      * @exception IOException Any exception thrown by the underlying OutputStream.
  167.      * @since     JDK1.1
  168.      */
  169.     public final void writeObject(Object obj)
  170.     throws IOException
  171.     {
  172.     Object prevObject = currentObject;
  173.     ObjectStreamClass prevClassDesc = currentClassDesc;
  174.     boolean oldBlockDataMode = setBlockData(false);
  175.     recursionDepth++;
  176.  
  177.     try {
  178.         if (serializeNullAndRepeat(obj))
  179.         return;
  180.  
  181.         if (checkSpecialClasses(obj))
  182.         return;
  183.  
  184.  
  185.         /* If the replacment is enabled, give subclasses one chance
  186.          * to substitute a new object. If one is substituted,
  187.          * recheck for null, repeated refs, and special cased classes
  188.          */
  189.         if (enableReplace) {
  190.         Object altobj = replaceObject(obj);
  191.         if (obj != altobj) {
  192.  
  193.             if (altobj != null && !(altobj instanceof Serializable)) {
  194.             String clname = altobj.getClass().getName();
  195.             throw new NotSerializableException(clname);
  196.             }
  197.             
  198.             // If the alternate object is already
  199.             // serialized just remember the replacement
  200.             if (serializeNullAndRepeat(altobj)) {
  201.             addReplacement(obj, altobj);
  202.             return;
  203.             }
  204.  
  205.             /* Add this to the set of replaced objects.
  206.              * This must be done before the object is
  207.              * serialized so that if the object indirectly
  208.              * refers to the original it will be redirected to
  209.              * the replacement.
  210.              *
  211.              * NB: checkSpecialClasses should only call
  212.              * serializeNullandRepeat for objects that will not
  213.              * recurse.
  214.              */
  215.             addReplacement(obj, altobj);
  216.  
  217.             if (checkSpecialClasses(altobj))
  218.             return;
  219.  
  220.             obj = altobj;
  221.         }
  222.         }
  223.         /* Write out the object as itself */
  224.         outputObject(obj);
  225.     } catch (ObjectStreamException ee) {
  226.         if (abortIOException == null) {
  227.         try {
  228.             /* Prepare to write the exception to the stream.
  229.              * End blockdatamode in case it's set
  230.              * Write the exception code
  231.              * reset the stream to forget all previous objects
  232.              * write the exception that occurred
  233.              * reset the stream again so subsequent objects won't map to
  234.              * the exception or its args.
  235.              * Continue below to rethrow the exception.
  236.              */
  237.             setBlockData(false);
  238.  
  239.             writeCode(TC_EXCEPTION);
  240.             resetStream();
  241.             this.writeObject(ee);
  242.             resetStream();
  243.  
  244.             // Set the pending exception to be rethrown.
  245.             abortIOException = ee;
  246.         } catch (IOException fatal) {
  247.             /* An exception occurred while writing the original exception to
  248.              * the stream.  The original exception is not complete in
  249.              * the stream and recusion would be bad. Supercede the original
  250.              * Exception with a StreamCorruptedException using the message
  251.              * from this current exception.
  252.              */
  253.             abortIOException =
  254.             new StreamCorruptedException(fatal.getMessage());
  255.         }
  256.         }
  257.     } catch (IOException ee) {
  258.         // Don't supercede a pending exception, the original will be re-thrown.
  259.         if (abortIOException == null)
  260.         abortIOException = ee;
  261.         
  262.     } finally {
  263.         /* Restore state of previous call incase this is a nested call */
  264.         recursionDepth--;
  265.         currentObject = prevObject;
  266.         currentClassDesc = prevClassDesc;
  267.         setBlockData(oldBlockDataMode);
  268.     }
  269.     
  270.     /* If the recursion depth is 0, test for and clear the pending exception.
  271.      * If there is a pending exception throw it.
  272.      */
  273.     IOException pending = abortIOException;
  274.     if (recursionDepth == 0)
  275.         abortIOException = null;
  276.     if (pending != null) {
  277.         throw pending;
  278.     }
  279.     }
  280.     
  281.     /*
  282.      * Check for special cases of serializing objects.
  283.      */
  284.     private boolean checkSpecialClasses(Object obj) throws IOException {
  285.  
  286.     /*
  287.      * If this is a class, don't allow substitution
  288.      */
  289.     if (obj instanceof Class) {
  290.         outputClass((Class)obj);
  291.         return true;
  292.     }
  293.  
  294.     if (obj instanceof ObjectStreamClass) {
  295.         outputClassDescriptor((ObjectStreamClass)obj);
  296.         return true;
  297.     }
  298.  
  299.     if (obj instanceof String) {
  300.         outputString((String)obj);
  301.         return true;
  302.     }
  303.  
  304.     if (obj.getClass().isArray()) {
  305.         outputArray(obj);
  306.         return true;
  307.     }
  308.     return false;
  309.     }
  310.  
  311.     /**
  312.      * Write the non-static and non-transient fields of the current class
  313.      * to this stream.  This may only be called from the writeObject method
  314.      * of the class being serialized. It will throw the NotActiveException
  315.      * if it is called otherwise.
  316.      * @since     JDK1.1
  317.      */
  318.     public final void defaultWriteObject() throws IOException {
  319.     if (currentObject == null || currentClassDesc == null)
  320.         throw new NotActiveException("defaultWriteObject");
  321.     
  322.     if (currentClassDesc.getFieldSequence() != null) {
  323.         boolean prevmode = setBlockData(false);
  324.         outputClassFields(currentObject, currentClassDesc.forClass(),
  325.                   currentClassDesc.getFieldSequence());
  326.         setBlockData(prevmode);
  327.     }
  328.     }
  329.     
  330.     /**
  331.      * Reset will disregard the state of any objects already written
  332.      * to the stream.  The state is reset to be the same as a new
  333.      * ObjectOutputStream.  The current point in the stream is marked
  334.      * as reset so the corresponding ObjectInputStream will be reset
  335.      * at the same point.  Objects previously written to the stream
  336.      * will not be refered to as already being in the stream.  They
  337.      * will be written to the stream again.
  338.      * @since     JDK1.1
  339.      */
  340.     public void reset() throws IOException {
  341.     if (currentObject != null || currentClassDesc != null)
  342.         throw new IOException("Illegal call to reset");
  343.     
  344.     /* Write a reset to the stream. */        
  345.     setBlockData(false);
  346.     writeCode(TC_RESET);
  347.     
  348.     resetStream();            // re-init the stream
  349.     abortIOException = null;
  350.     }
  351.  
  352.     /*
  353.      * Internal reset function to reinitialize the state of the stream.
  354.      * Reset state of things changed by using the stream.
  355.      */
  356.     private void resetStream() throws IOException {
  357.     wireHandle2Object = new Object[100];
  358.     wireNextHandle = new int[100];
  359.     wireHash2Handle = new int[101];
  360.     for (int i = 0; i < wireHash2Handle.length; i++) {
  361.         wireHash2Handle[i] = -1;
  362.     }
  363.     classDescStack = new Stack();
  364.     nextWireOffset = 0;
  365.     replaceObjects = null;
  366.     nextReplaceOffset = 0;
  367.     setBlockData(true);        /* Re-enable buffering */
  368.     }
  369.     
  370.     /**
  371.      * Subclasses may implement this method to allow class data to be stored
  372.      * in the stream. By default this method does nothing.
  373.      * The corresponding method in ObjectInputStream is resolveClass.
  374.      * This method is called exactly once for each unique class in the stream.
  375.      * The class name and signature will have already been written to the stream.
  376.      * This method may make free use of the ObjectOutputStream to save
  377.      * any representation of the class it deems suitable (for example,
  378.      * the bytes of the class file).  The resolveClass method in the corresponding
  379.      * subclass of ObjectInputStream must read and use any data or objects
  380.      * written by annotateClass. 
  381.      * annotateClass is called only for normal classes.  Arrays are not normal classes.
  382.      * @exception IOException Any exception thrown by the underlying OutputStream.
  383.      * @since     JDK1.1
  384.      */
  385.     protected void annotateClass(Class cl)
  386.     throws IOException
  387.     {
  388.     }
  389.  
  390.     /** This method will allow trusted subclasses of ObjectOutputStream
  391.      * to substitute one object for another during
  392.      * serialization. Replacing objects is disabled until
  393.      * enableReplaceObject is called. The enableReplaceObject method
  394.      * checks that the stream requesting to do replacment can be
  395.      * trusted. Every reference to serializable objects is passed to
  396.      * replaceObject.  To insure that the private state of objects is
  397.      * not unintentionally exposed only trusted streams may use
  398.      * replaceObject. <p>
  399.      *
  400.      * When a subclass is replacing objects it must insure that either
  401.      * a complementary substitution must be made during
  402.      * deserialization or that the substituted object is compatible
  403.      * with every field where the reference will be stored.  Objects
  404.      * whose type is not a subclass of the type of the field or array
  405.      * element abort the serialization by raising an exception and the
  406.      * object is not be stored. <p>
  407.      *
  408.      * This method is called only once when each object is first encountered.
  409.      * All subsequent references to the object will be redirected to the
  410.      * new object. This method should return the object to be substituted or
  411.      * the original object. <P>
  412.      *
  413.      * Null can be returned as the object to be substituted, but may
  414.      * cause NullReferenceException in classes that contain references
  415.      * to the original object since they may be expecting an object
  416.      * instead of null.<p>
  417.      *
  418.      * @exception IOException Any exception thrown by the underlying
  419.      * OutputStream.
  420.      * @since     JDK1.1
  421.      */
  422.     protected Object replaceObject(Object obj)
  423.     throws IOException
  424.     {
  425.     return obj;
  426.     }
  427.  
  428.     /**
  429.      * Enable the stream to do replacement of objects in the stream.
  430.      * If the stream is a trusted class it is allowed to enable replacement.
  431.      * Trusted classes are those classes with a classLoader equals null. <p>
  432.      * 
  433.      * When enabled the replaceObject method is called for every object
  434.      * being serialized.
  435.      * 
  436.      * @exception SecurityException The classloader of this stream object is non-null.
  437.      * @since     JDK1.1
  438.      */
  439.     protected final boolean enableReplaceObject(boolean enable)
  440.     throws SecurityException
  441.     {
  442.     boolean previous = enableReplace;
  443.     if (enable) {
  444.         ClassLoader loader = this.getClass().getClassLoader();
  445.         if (loader == null) {
  446.         enableReplace = true;
  447.         return previous;
  448.         }
  449.         throw new SecurityException("Not trusted class");
  450.     } else {
  451.         enableReplace = false;
  452.     }
  453.     return previous;
  454.     }
  455.  
  456.     /**
  457.      * The writeStreamHeader method is provided so subclasses can
  458.      * append or prepend their own header to the stream.
  459.      * It writes the magic number and version to the stream.
  460.      * @since     JDK1.1
  461.      */
  462.     protected void writeStreamHeader() throws IOException {
  463.     writeShort(STREAM_MAGIC);
  464.     writeShort(STREAM_VERSION);
  465.     }
  466.  
  467.     /**
  468.      * Write a string to the stream.
  469.      * Note that since Strings are Objects, writeObject
  470.      * will behave identically.
  471.      */
  472.     private void outputString(String s) throws IOException {
  473.     /* Allocate a write handle but don't write it to the stream,
  474.      * Write out the code for a string,
  475.      * the read can regenerate the same sequence and it saves bytes.
  476.      */
  477.     assignWireOffset(s);
  478.     writeCode(TC_STRING);
  479.     writeUTF(s); 
  480.     }
  481.  
  482.  
  483.     /* Classes are special, they can not created during deserialization,
  484.      * but the appropriate class can be found. 
  485.      */
  486.     private void outputClass(Class aclass) throws IOException {
  487.  
  488.     writeCode(TC_CLASS);
  489.     /* Find the class descriptor and write it out */
  490.     ObjectStreamClass v = ObjectStreamClass.lookup(aclass);
  491.  
  492.     if (v == null)
  493.         throw new NotSerializableException(aclass.getName());
  494.  
  495.     outputClassDescriptor(v);
  496.  
  497.     assignWireOffset(aclass);
  498.     }
  499.   
  500.  
  501.     /* Write the class descriptor */
  502.     private void outputClassDescriptor(ObjectStreamClass classdesc) 
  503.     throws IOException
  504.     {
  505.     if (serializeNullAndRepeat(classdesc))
  506.         return;
  507.  
  508.     /* Write out the code for a class
  509.      * Write out the class name and its serialVersionUID
  510.      */
  511.     writeCode(TC_CLASSDESC);
  512.     String classname = classdesc.getName();
  513.  
  514.     writeUTF(classname);
  515.     writeLong(classdesc.getSerialVersionUID());
  516.  
  517.     /* This is done here to be symetric with the inputClass method
  518.      * Since the resolveClassName() method may use the stream.
  519.      * The assignments of wirehandles must be done in the same order
  520.      */
  521.     assignWireOffset(classdesc);
  522.  
  523.     /* Write the version description for this class */
  524.     classdesc.write(this);
  525.  
  526.     /* Give subclassers a chance to add the class implementation
  527.      * to the stream.  Set BlockData mode so any information they
  528.      * write can be skipped on reading.
  529.      */
  530.     boolean prevMode = setBlockData(true);
  531.     annotateClass(classdesc.forClass());
  532.     setBlockData(prevMode);
  533.     writeCode(TC_ENDBLOCKDATA);
  534.  
  535.     /*
  536.      * Write out the superclass descriptor of this descriptor
  537.      * only if it is for a java.io.Serializable class.
  538.      * else write null.
  539.      */
  540.     ObjectStreamClass superdesc = classdesc.getSuperclass();
  541.     outputClassDescriptor(superdesc);
  542.     }
  543.     
  544.     /**
  545.      * Write an array out. Note that since Arrays are Objects, writeObject(obj)
  546.      * will behave identically. <br><br>
  547.      * @param o can represent an array of any type/dimension.
  548.      */
  549.     private void outputArray(Object obj)
  550.     throws IOException
  551.     {
  552.     Class currclass = obj.getClass();
  553.  
  554.     ObjectStreamClass v = ObjectStreamClass.lookup(currclass);
  555.  
  556.     /* Write out the code for an array and the name of the class */
  557.     writeCode(TC_ARRAY);
  558.     outputClassDescriptor(v);
  559.  
  560.     /* Assign the wirehandle for this object and outputArrayValues
  561.      * writes the length and the array contents.
  562.      */
  563.     assignWireOffset(obj);
  564.  
  565.     int i, length;
  566.     Class type = currclass.getComponentType();
  567.  
  568.     if (type.isPrimitive()) {
  569.         /* Write arrays of primitive types using the DataOutput
  570.          * methods that convert each element into the output buffer.
  571.          * The data types are ordered by the frequency
  572.          * in which they are expected to occur.
  573.          */
  574.         if (type == Integer.TYPE) {
  575.         int[] array = (int[])obj;
  576.         length = array.length;
  577.         writeInt(length);
  578.         for (i = 0; i < length; i++) {
  579.             writeInt(array[i]);
  580.         }
  581.         } else if (type == Byte.TYPE) {
  582.         byte[] array = (byte[])obj;
  583.         length = array.length;
  584.         writeInt(length);
  585.         write(array, 0, length);
  586.         } else if (type == Long.TYPE) {
  587.         long[] array = (long[])obj;
  588.         length = array.length;
  589.         writeInt(length);
  590.         for (i = 0; i < length; i++) {
  591.             writeLong(array[i]);
  592.         }
  593.         } else if (type == Float.TYPE) {
  594.         float[] array = (float[])obj;
  595.         length = array.length;
  596.         writeInt(length);
  597.         for (i = 0; i < length; i++) {
  598.             writeFloat(array[i]);
  599.         }
  600.         } else if (type == Double.TYPE) {
  601.         double[] array = (double[])obj;
  602.         length = array.length;
  603.         writeInt(length);
  604.         for (i = 0; i < length; i++) {
  605.             writeDouble(array[i]);
  606.         }
  607.         } else if (type == Short.TYPE) {
  608.         short[] array = (short[])obj;
  609.         length = array.length;
  610.         writeInt(length);
  611.         for (i = 0; i < length; i++) {
  612.             writeShort(array[i]);
  613.         }
  614.         } else if (type == Character.TYPE) {
  615.         char[] array = (char[])obj;
  616.         length = array.length;
  617.         writeInt(length);
  618.         for (i = 0; i < length; i++) {
  619.             writeChar(array[i]);
  620.         }
  621.         } else if (type == Boolean.TYPE) {
  622.         boolean[] array = (boolean[])obj;
  623.         length = array.length;
  624.         writeInt(length);
  625.         for (i = 0; i < length; i++) {
  626.             writeBoolean(array[i]);
  627.         }
  628.         } else {
  629.         throw new InvalidClassException(currclass.getName());
  630.         }
  631.     } else {
  632.         Object[] array = (Object[])obj;
  633.         length = array.length;
  634.         writeInt(length);
  635.         for (i = 0; i < length; i++) {
  636.         writeObject(array[i]);
  637.         }
  638.     }
  639.     }
  640.  
  641.     /*
  642.      * Put the object into the stream The newObject code is written
  643.      * followed by the ObjectStreamClass for the object's class.  Each
  644.      * of the objects classes is written using the default
  645.      * serialization code and dispatching to Specials where
  646.      * appropriate.
  647.      */
  648.     private void outputObject(Object obj)
  649.     throws IOException
  650.     {
  651.     currentObject = obj;
  652.     Class currclass = obj.getClass();
  653.  
  654.     /* Get the Class descriptor for this class,
  655.      * Throw a NotSerializableException if there is none.
  656.      */
  657.     currentClassDesc = ObjectStreamClass.lookup(currclass);
  658.     if (currentClassDesc == null) {
  659.         throw new NotSerializableException(currclass.getName());
  660.     }
  661.  
  662.     /* Write the code to expect an instance and
  663.      * the class descriptor of the instance
  664.      */
  665.     writeCode(TC_OBJECT);
  666.     outputClassDescriptor(currentClassDesc);
  667.  
  668.     /* Assign the next wirehandle */
  669.     assignWireOffset(obj);
  670.  
  671.     /* If the object is externalizable,
  672.      * call writeExternal.
  673.      * else do Serializable processing.
  674.      */
  675.     if (currentClassDesc.isExternalizable()) {
  676.         Externalizable ext = (Externalizable)obj;
  677.         ext.writeExternal(this);
  678.     } else {
  679.  
  680.         /* The object's classes should be processed from supertype to subtype
  681.          * Push all the clases of the current object onto a stack.
  682.          * Remember the stack pointer where this set of classes is being pushed.
  683.          */
  684.         int stackMark = classDescStack.size();
  685.         try {
  686.         ObjectStreamClass next;
  687.         while ((next = currentClassDesc.getSuperclass()) != null) {
  688.             classDescStack.push(currentClassDesc);
  689.             currentClassDesc = next;
  690.         }
  691.  
  692.         /* 
  693.          * For currentClassDesc and all the pushed class descriptors
  694.          *    If the class is writing its own data
  695.          *          set blockData = true; call the class writeObject method
  696.          *    If not
  697.          *     invoke either the defaultWriteObject method.
  698.          */
  699.         do {
  700.             if (currentClassDesc.hasWriteObject()) {
  701.             setBlockData(true); /* Block any data the class writes */
  702.             invokeObjectWriter(obj, currentClassDesc.forClass());
  703.             setBlockData(false);
  704.             writeCode(TC_ENDBLOCKDATA);
  705.             } else {
  706.             defaultWriteObject();
  707.             }
  708.         } while (classDescStack.size() > stackMark &&
  709.              (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
  710.         } finally {
  711.         classDescStack.setSize(stackMark);
  712.         }
  713.     }
  714.     }
  715.     
  716.     /* Serialize the reference if it is NULL or is for an object that
  717.      * was already replaced or already serialized.
  718.      * If the object was already replaced, look for the replacement
  719.      * object in the known objects and if found, write its handle
  720.      * Return True if the reference is either null or a repeat.
  721.      */
  722.     private boolean serializeNullAndRepeat(Object obj)
  723.     throws IOException
  724.     {
  725.         if (obj == null) {
  726.         writeCode(TC_NULL);
  727.         return true;
  728.     }
  729.     /* Look to see if this object has already been replaced.
  730.      * If so, proceed using the replacement object.
  731.      */
  732.     if (replaceObjects != null) {
  733.         for (int i = 0; i < nextReplaceOffset; i+= 2) {
  734.         if (replaceObjects[i] == obj) {
  735.             obj = replaceObjects[i+1];
  736.             break;
  737.         }
  738.         }
  739.     }
  740.  
  741.     int handle = findWireOffset(obj);
  742.     if (handle >= 0) {
  743.         /* Add a reference to the stream */
  744.         writeCode(TC_REFERENCE);
  745.         writeInt(handle + baseWireHandle);
  746.         return true;
  747.     }
  748.     return false;        // not serialized, its up to the caller
  749.     }
  750.  
  751.     /*
  752.      * Locate and return if found the handle for the specified object.
  753.      * -1 is returned if the object does not occur in the array of
  754.      * known objects. 
  755.      */
  756.     private int findWireOffset(Object obj) {
  757.     int hash = System.identityHashCode(obj);
  758.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  759.  
  760.     for (int handle = wireHash2Handle[index];
  761.          handle >= 0;
  762.          handle = wireNextHandle[handle]) {
  763.         
  764.         if (wireHandle2Object[handle] == obj)
  765.         return handle;
  766.     }
  767.     return -1;
  768.     }
  769.  
  770.     /* Allocate a handle for an object.
  771.      * The Vector is indexed by the wireHandleOffset
  772.      * and contains the object.
  773.      */
  774.     private void assignWireOffset(Object obj)
  775.     throws IOException
  776.     {
  777.     // Extend the array if there isn't room for this new element
  778.     if (nextWireOffset == wireHandle2Object.length) {
  779.         Object[] oldhandles = wireHandle2Object;
  780.         wireHandle2Object = new Object[nextWireOffset*2];
  781.         System.arraycopy(oldhandles, 0,
  782.                  wireHandle2Object, 0,
  783.                  nextWireOffset);
  784.         int[] oldnexthandles = wireNextHandle;
  785.         wireNextHandle = new int[nextWireOffset*2];
  786.         System.arraycopy(oldnexthandles, 0,
  787.                  wireNextHandle, 0,
  788.                  nextWireOffset);
  789.         // TBD: Rehash the hash array if necessary
  790.     }
  791.     wireHandle2Object[nextWireOffset] = obj;
  792.  
  793.     hashInsert(obj, nextWireOffset);
  794.  
  795.     nextWireOffset++;
  796.     return;
  797.     }
  798.  
  799.  
  800.     /*
  801.      * Insert the specified object into the hash array and link if
  802.      * necessary. Put the new object into the hash table and link the
  803.      * previous to it. Newer objects occur earlier in the list.
  804.      */
  805.     private void hashInsert(Object obj, int offset) {
  806.     int hash = System.identityHashCode(obj);
  807.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  808.     wireNextHandle[offset] = wireHash2Handle[index];
  809.     wireHash2Handle[index] = offset;
  810.     }
  811.  
  812.     /*
  813.      * Add a replacement object to the table.
  814.      * The even numbered indices are the original objects.
  815.      * The odd numbered indices are the replacement objects.
  816.      *
  817.      */
  818.     private void addReplacement(Object orig, Object replacement) {
  819.     // Extend the array if there isn't room for this new element
  820.  
  821.     if (replaceObjects == null) {
  822.         replaceObjects = new Object[10];
  823.     }
  824.     if (nextReplaceOffset == replaceObjects.length) {
  825.         Object[] oldhandles = replaceObjects;
  826.         replaceObjects = new Object[2+nextReplaceOffset*2];
  827.         System.arraycopy(oldhandles, 0,
  828.                  replaceObjects, 0,
  829.                  nextReplaceOffset);
  830.     }
  831.     replaceObjects[nextReplaceOffset++] = orig;
  832.     replaceObjects[nextReplaceOffset++] = replacement;
  833.     }
  834.  
  835.     /* Write out the code indicating the type what follows.
  836.      * See ObjectStreamConstants for definitions.
  837.      */
  838.     private void writeCode(int tag)
  839.     throws IOException
  840.     {
  841.     writeByte(tag);
  842.     }
  843.  
  844.     /*
  845.      * Implement the OutputStream methods.  The stream has
  846.      * two modes used internally to ObjectOutputStream.  When
  847.      * in BlockData mode, all writes are buffered and written
  848.      * to the underlying stream prefixed by a code and length.
  849.      * When not in BlockData mode (false), writes pass directly
  850.      * through to the underlying stream.
  851.      * The BlockData mode is used to encapsulate data written
  852.      * by class specific writeObject methods that is intended
  853.      * only to be read by corresponding readObject method of the 
  854.      * same class.  The blocking of data allows it to be skipped
  855.      * if necessary.
  856.      *
  857.      * The setBlockData method is used to switch buffering
  858.      * on and off.  When switching between on and off
  859.      * the buffer is drained.
  860.      *
  861.      * The actual buffering is very similar to that of
  862.      * BufferedOutputStream but BufferedOutputStream can
  863.      * write to the underlying stream without the headers.
  864.      */
  865.     private boolean blockDataMode;    /* true to buffer and block data */
  866.     private byte[] buf;        /* byte array of buffered data. */
  867.     private int count;        /* count of bytes in the buffer */
  868.     private OutputStream out;    /* Stream to write the data to */
  869.  
  870.     /**
  871.      * Writes a byte. This method will block until the byte is actually
  872.      * written.
  873.      * @param b    the byte
  874.      * @exception IOException If an I/O error has occurred.
  875.      * @since     JDK1.1
  876.      */
  877.     public void write(int data) throws IOException {
  878.     
  879.     if (count >= buf.length)
  880.         drain();        /* Drain, make room for more */
  881.     buf[count++] = (byte)data;
  882.     }
  883.  
  884.     /**
  885.      * Writes an array of bytes. This method will block until the bytes
  886.      * are actually written.
  887.      * @param b    the data to be written
  888.      * @exception IOException If an I/O error has occurred.
  889.      * @since     JDK1.1
  890.      */
  891.     public void write(byte b[]) throws IOException {
  892.     write(b, 0, b.length);
  893.     }
  894.  
  895.     /**
  896.      * Writes a sub array of bytes. 
  897.      * @param b    the data to be written
  898.      * @param off    the start offset in the data
  899.      * @param len    the number of bytes that are written
  900.      * @exception IOException If an I/O error has occurred.
  901.      * @since     JDK1.1
  902.      */
  903.     public void write(byte b[], int off, int len) throws IOException {
  904.     if (len < 0)
  905.         throw new IndexOutOfBoundsException();
  906.  
  907.     /*
  908.      * If array will fit in output buffer, copy it in there; otherwise,
  909.      * drain anything in the buffer and send it through to underlying
  910.      * output stream directly.
  911.      */
  912.     int avail = buf.length - count;
  913.     if (len <= avail) {
  914.         System.arraycopy(b, off, buf, count, len);
  915.         count += len;
  916.     } else {
  917.         drain();
  918.         if (blockDataMode) {
  919.         if (len <= 255) {
  920.             out.write(TC_BLOCKDATA);
  921.             out.write(len);
  922.         } else {
  923.             // use block data with int size if necessary
  924.             out.write(TC_BLOCKDATALONG);
  925.             // send 32 bit int directly to underlying stream
  926.             out.write((len >> 24) & 0xFF);
  927.             out.write((len >> 16) & 0xFF);
  928.             out.write((len >>  8) & 0xFF);
  929.             out.write(len & 0xFF);
  930.         }
  931.         }
  932.         out.write(b, off, len);
  933.     }
  934.     }
  935.  
  936.  
  937.     /**
  938.      * Flushes the stream. This will write any buffered
  939.      * output bytes and flush through to the underlying stream.
  940.      * @exception IOException If an I/O error has occurred.
  941.      * @since     JDK1.1
  942.      */
  943.     public void flush() throws IOException {
  944.     drain();
  945.     out.flush();
  946.     }
  947.  
  948.     /**
  949.      * Drain any buffered data in ObjectOutputStream.  Similar to flush
  950.      * but does not propagate the flush to the underlaying stream.
  951.      * @since     JDK1.1
  952.      */
  953.     protected void drain() throws IOException {
  954.     /*
  955.      * Drain the data buffer.
  956.      * If the blocking mode is on, prepend the buffer
  957.      * with the code for a blocked data and the length.
  958.      * The code is TC_BLOCKDATA and the length is < 255 so it fits
  959.      * in a byte. 
  960.      */
  961.     if (count == 0)
  962.         return;
  963.  
  964.     /* If in blockdata mode, write the header with the count.
  965.      * If the count is < 256, use the short header form.
  966.      * othewise use the long form.
  967.      */
  968.     if (blockDataMode) {
  969.         if (count <= 255) {
  970.         out.write(TC_BLOCKDATA);
  971.         out.write(count);
  972.         } else {
  973.         out.write(TC_BLOCKDATALONG);
  974.         out.write((count >> 24) & 0xFF);
  975.         out.write((count >> 16) & 0xFF);
  976.         out.write((count >>  8) & 0xFF);
  977.         out.write(count & 0xFF);
  978.         }
  979.     }
  980.     out.write(buf, 0, count);
  981.     count = 0;
  982.     }
  983.  
  984.     /**
  985.      * Closes the stream. This method must be called
  986.      * to release any resources associated with the
  987.      * stream.
  988.      * @exception IOException If an I/O error has occurred.
  989.      * @since     JDK1.1
  990.      */
  991.     public void close() throws IOException {
  992.     flush();        /* Make sure we're not holding any data */
  993.     out.close();
  994.     }
  995.  
  996.     /*
  997.      * Set the blockData mode,  if it turned from on to off
  998.      * the buffer is drained.  The previous mode is returned.
  999.      */
  1000.     private boolean setBlockData(boolean mode) throws IOException {
  1001.     if (blockDataMode == mode)
  1002.         return mode;
  1003.     drain();
  1004.     blockDataMode = mode;
  1005.     return !mode;        /* previous value was the opposite */
  1006.     }
  1007.     
  1008.     /* -------------------------------------------------------------- */
  1009.     /*
  1010.      * Provide the methods to implement DataOutput.
  1011.      * These are copied from DataOutputStream to avoid the overhead
  1012.      * of multiple method calls and to buffer the data directly.
  1013.      */
  1014.     private DataOutputStream dos;
  1015.  
  1016.     /**
  1017.      * Writes a boolean.
  1018.      * @param data the boolean to be written
  1019.      * @since     JDK1.1
  1020.      */
  1021.     public void writeBoolean(boolean data) throws IOException {
  1022.     if (count >= buf.length)
  1023.         drain();
  1024.     buf[count++] = (byte)(data ? 1 : 0);
  1025.  
  1026.     }
  1027.  
  1028.     /**
  1029.      * Writes an 8 bit byte.
  1030.      * @param data the byte value to be written
  1031.      * @since     JDK1.1
  1032.      */
  1033.     public void writeByte(int data) throws IOException  {
  1034.     if (count >= buf.length)
  1035.         drain();
  1036.     buf[count++] = (byte)data;
  1037.     }
  1038.  
  1039.     /**
  1040.      * Writes a 16 bit short.
  1041.      * @param data the short value to be written
  1042.      * @since     JDK1.1
  1043.      */
  1044.     public void writeShort(int data)  throws IOException {
  1045.     if (count + 2 > buf.length)
  1046.         drain();
  1047.     buf[count++] = (byte)((data >>>  8));
  1048.     buf[count++] = (byte)((data >>>  0));
  1049.     }
  1050.  
  1051.     /**
  1052.      * Writes a 16 bit char.
  1053.      * @param data the char value to be written
  1054.      * @since     JDK1.1
  1055.      */
  1056.     public void writeChar(int data)  throws IOException {
  1057.     if (count + 2 > buf.length)
  1058.         drain();
  1059.     buf[count++] = (byte)((data >>>  8));
  1060.     buf[count++] = (byte)((data >>>  0));
  1061.     }
  1062.  
  1063.     /**
  1064.      * Writes a 32 bit int.
  1065.      * @param data the integer value to be written
  1066.      * @since     JDK1.1
  1067.      */
  1068.     public void writeInt(int data)  throws IOException {
  1069.     if (count + 4 > buf.length)
  1070.         drain();
  1071.     buf[count++] = (byte)((data >>> 24));
  1072.     buf[count++] = (byte)((data >>> 16));
  1073.     buf[count++] = (byte)((data >>>  8));
  1074.     buf[count++] = (byte)((data >>>  0));
  1075.     }
  1076.  
  1077.     /**
  1078.      * Writes a 64 bit long.
  1079.      * @param data the long value to be written
  1080.      * @since     JDK1.1
  1081.      */
  1082.     public void writeLong(long data)  throws IOException {
  1083.     if (count + 8 > buf.length)
  1084.         drain();
  1085.     buf[count++] = (byte)((int)(data >>> 56));
  1086.     buf[count++] = (byte)((int)(data >>> 48));
  1087.     buf[count++] = (byte)((int)(data >>> 40));
  1088.     buf[count++] = (byte)((int)(data >>> 32));
  1089.     buf[count++] = (byte)((data >>> 24));
  1090.     buf[count++] = (byte)((data >>> 16));
  1091.     buf[count++] = (byte)((data >>>  8));
  1092.     buf[count++] = (byte)((data >>>  0));
  1093.     }
  1094.  
  1095.     /**
  1096.      * Writes a 32 bit float.
  1097.      * @param data the float value to be written
  1098.      * @since     JDK1.1
  1099.      */
  1100.     public void writeFloat(float data) throws IOException {
  1101.     int value = Float.floatToIntBits(data);
  1102.     if (count + 4 > buf.length)
  1103.         drain();
  1104.     buf[count++] = (byte)((value >>> 24));
  1105.     buf[count++] = (byte)((value >>> 16));
  1106.     buf[count++] = (byte)((value >>>  8));
  1107.     buf[count++] = (byte)((value >>>  0));
  1108.     }
  1109.  
  1110.     /**
  1111.      * Writes a 64 bit double.
  1112.      * @param data the double value to be written
  1113.      * @since     JDK1.1
  1114.      */
  1115.     public void writeDouble(double data) throws IOException {
  1116.     long value = Double.doubleToLongBits(data);
  1117.     if (count + 8 > buf.length)
  1118.         drain();
  1119.     buf[count++] = (byte)((int)(value >>> 56));
  1120.     buf[count++] = (byte)((int)(value >>> 48));
  1121.     buf[count++] = (byte)((int)(value >>> 40));
  1122.     buf[count++] = (byte)((int)(value >>> 32));
  1123.     buf[count++] = (byte)((value >>> 24));
  1124.     buf[count++] = (byte)((value >>> 16));
  1125.     buf[count++] = (byte)((value >>>  8));
  1126.     buf[count++] = (byte)((value >>>  0));
  1127.     }
  1128.  
  1129.     /**
  1130.      * Writes a String as a sequence of bytes.
  1131.      * @param s the String of bytes to be written
  1132.      * @since     JDK1.1
  1133.      */
  1134.     public void writeBytes(String data) throws IOException {
  1135.     dos.writeBytes(data);
  1136.     }
  1137.  
  1138.     /**
  1139.      * Writes a String as a sequence of chars.
  1140.      * @param s the String of chars to be written
  1141.      * @since     JDK1.1
  1142.      */
  1143.     public void writeChars(String data) throws IOException {
  1144.     dos.writeChars(data);
  1145.     }
  1146.  
  1147.     /**
  1148.      * Writes a String in UTF format.
  1149.      * @param str the String in UTF format
  1150.      * @since     JDK1.1
  1151.      */
  1152.     public void writeUTF(String data) throws IOException {
  1153.     dos.writeUTF(data);
  1154.     }
  1155.     
  1156.     /*************************************************************/
  1157.     
  1158.     /* Remember the first exception that stopped this stream. */
  1159.     private IOException abortIOException = null;
  1160.     
  1161.     /* Write the fields of the specified class.
  1162.      * The native implemention sorts the field names to put them
  1163.      * in cononical order, ignores transient and static fields
  1164.      * and invokes the appropriate write* method on this class.
  1165.      */
  1166.     private native void outputClassFields(Object o, Class cl,
  1167.                       int[] fieldSequence)
  1168.     throws IOException, InvalidClassException;
  1169.  
  1170.     /* Test if Read/WriteObject methods are present, if so
  1171.      * invoke writer and return true.
  1172.      */
  1173.     private native boolean invokeObjectWriter(Object o, Class c)
  1174.     throws IOException;
  1175.  
  1176.     /* Object references are mapped to the wire handles through a hashtable
  1177.      * WireHandles are integers generated by the ObjectOutputStream,
  1178.      * they need only be unique within a stream.
  1179.      * Objects are assigned sequential handles and stored in wireHandle2Object.
  1180.      * The handle for an object is its index in wireHandle2Object.
  1181.      * Object with the "same" hashcode are chained using wireHash2Handle.
  1182.      * The hashcode of objects is used to index through the wireHash2Handle.
  1183.      * -1 is the marker for unused cells in wireNextHandle
  1184.      */
  1185.     private Object[] wireHandle2Object;
  1186.     private int[] wireNextHandle;
  1187.     private int[] wireHash2Handle;
  1188.     private int nextWireOffset;
  1189.  
  1190.     /* The object is the current object and ClassDescriptor is the current
  1191.      * subclass of the object being read. Nesting information is kept
  1192.      * on the stack.
  1193.      */
  1194.     private Object currentObject;
  1195.     private ObjectStreamClass currentClassDesc;
  1196.     private Stack classDescStack;
  1197.  
  1198.     /* 
  1199.      * Flag set to true to allow replaceObject to be called.
  1200.      * Set by enableReplaceObject.
  1201.      * The array of replaceObjects and the index of the next insertion.
  1202.      */
  1203.     private boolean enableReplace;
  1204.     private Object[] replaceObjects;
  1205.     private int nextReplaceOffset;
  1206.  
  1207.     /* Recursion level, starts at zero and is incremented for each entry
  1208.      * to writeObject.  Decremented before exit.
  1209.      */ 
  1210.     private int recursionDepth = 0;
  1211. }
  1212.