home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / io / ObjectOutputStream.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  55.1 KB  |  1,769 lines

  1. /*
  2.  * @(#)ObjectOutputStream.java    1.64 98/03/18
  3.  *
  4.  * Copyright 1996-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.io;
  16.  
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.util.Stack;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.security.AccessController;
  21.  
  22. /**
  23.  * An ObjectOutputStream writes primitive data types and graphs of
  24.  * Java objects to an OutputStream.  The objects can be read
  25.  * (reconstituted) using an ObjectInputStream.
  26.  * Persistent storage of objects can be accomplished by using a file for
  27.  * the stream.
  28.  * If the stream is a network socket stream, the objects can be reconsituted
  29.  * on another host or in another process. <p>
  30.  *
  31.  * Only objects that support the java.io.Serializable interface can be
  32.  * written to streams.
  33.  *
  34.  * The class of each serializable object is encoded including the class
  35.  * name and signature of the class, the values of the
  36.  * object's fields and arrays, and the closure of any other objects
  37.  * referenced from the initial objects. <p>
  38.  * 
  39.  * The method <STRONG>writeObject</STRONG> is used to write an object
  40.  * to the stream.  Any object, including Strings and arrays, is
  41.  * written with writeObject. Multiple objects or primitives can be
  42.  * written to the stream.  The objects must be read back from the
  43.  * corresponding ObjectInputstream with the same types and in the same
  44.  * order as they were written.<p>
  45.  *
  46.  * Primitive data types can also be written to the stream using the
  47.  * appropriate methods from DataOutput. Strings can also be written
  48.  * using the writeUTF method.<p>
  49.  *
  50.  * The default serialization mechanism for an object writes the class
  51.  * of the object, the class signature, and the values of all
  52.  * non-transient and non-static fields.  References to other objects
  53.  * (except in transient or static fields) cause those objects to be
  54.  * written also. Multiple references to a single object are encoded
  55.  * using a reference sharing mechanism so that graphs of objects can
  56.  * be restored to the same shape as when the original was written. <p>
  57.  *
  58.  * For example to write an object that can be read by the example in ObjectInputStream: <br>
  59.  * <PRE>
  60.  *    FileOutputStream ostream = new FileOutputStream("t.tmp");
  61.  *    ObjectOutputStream p = new ObjectOutputStream(ostream);
  62.  *
  63.  *    p.writeInt(12345);
  64.  *    p.writeObject("Today");
  65.  *    p.writeObject(new Date());
  66.  *
  67.  *    p.flush();
  68.  *    ostream.close();
  69.  *
  70.  * </PRE>
  71.  *
  72.  * Classes that require special handling during the serialization and deserialization
  73.  * process must implement special methods with these exact signatures: <p>
  74.  *
  75.  * <PRE>
  76.  * private void readObject(java.io.ObjectInputStream stream)
  77.  *     throws IOException, ClassNotFoundException; 
  78.  * private void writeObject(java.io.ObjectOutputStream stream)
  79.  *     throws IOException
  80.  * </PRE><p>
  81.  * The writeObject method is responsible for writing the state of
  82.  * the object for its particular class so that the corresponding
  83.  * readObject method can restore it.
  84.  * The method does not need to concern itself with the
  85.  * state belonging to the object's superclasses or subclasses.
  86.  * State is saved by writing the individual fields to the ObjectOutputStream
  87.  * using the writeObject method or by using the methods for
  88.  * primitive data types supported by DataOutput. <p>
  89.  *
  90.  * Serialization does not write out the fields of any object that does
  91.  * not implement the java.io.Serializable interface.  Subclasses of
  92.  * Objects that are not serializable can be serializable. In this case
  93.  * the non-serializable class must have a no-arg constructor to allow
  94.  * its fields to be initialized.  In this case it is the
  95.  * responsibility of the subclass to save and restore the state of the
  96.  * non-serializable class. It is frequently the case that the fields
  97.  * of that class are accessible (public, package, or protected) or
  98.  * that there are get and set methods that can be used to restore the
  99.  * state. <p>
  100.  *
  101.  * Serialization of an object can be prevented by implementing writeObject
  102.  * and readObject methods that throw the NotSerializableException.
  103.  * The exception will be caught by the ObjectOutputStream and abort the
  104.  * serialization process.
  105.  *
  106.  * Implementing the Externalizable interface allows the object to
  107.  * assume complete control over the contents and format of the object's
  108.  * serialized form.  The methods of the Externalizable interface,
  109.  * writeExternal and readExternal, are called to save and restore the
  110.  * objects state.  When implemented by a class they can write and read
  111.  * their own state using all of the methods of ObjectOutput and
  112.  * ObjectInput.  It is the responsibility of the objects to handle any
  113.  * versioning that occurs.
  114.  *
  115.  * Primitive data, excluding serializable fields and externalizable
  116.  * data, is written to the ObjectOutputStream in block-data
  117.  * records. A block data record is composed of a header and
  118.  * data. The block data header consists of a marker and the
  119.  * number of bytes to follow the header.  Consecutive primitive data
  120.  * writes are merged into one block-data record. 
  121.  *
  122.  *  (*) The blocking factor used for a block-data record will 
  123.  *      be 1024 bytes.
  124.  *    
  125.  *  (*) Each block-data record will be filled up to 1024 bytes, or be
  126.  *      written whenever there is a termination of block-data mode.
  127.  *
  128.  *  Calls to the ObjectOutputStream methods writeObject,
  129.  *  defaultWriteObject and writeFields initially terminate any
  130.  *  existing block-data record.
  131.  *
  132.  * @author    Roger Riggs
  133.  * @version     1.64, 03/18/98
  134.  * @see java.io.DataOutput
  135.  * @see java.io.ObjectInputStream
  136.  * @see java.io.Serializable
  137.  * @see java.io.Externalizable
  138.  * @since       JDK1.1
  139.  */
  140. public class ObjectOutputStream
  141.     extends OutputStream
  142.     implements ObjectOutput, ObjectStreamConstants
  143.             
  144.     /** 
  145.      * Creates an ObjectOutputStream that writes to the specified OutputStream.
  146.      * The stream header is written to the stream. The caller may want to call
  147.      * flush immediately so that the corresponding ObjectInputStream can read
  148.      * the header immediately.
  149.      *
  150.      * @exception IOException Any exception thrown by the underlying OutputStream.
  151.  
  152.      */
  153.     public ObjectOutputStream(OutputStream out) throws IOException {
  154.     enableSubclassImplementation = false;
  155.     this.out = out;
  156.     dos = new DataOutputStream(this);
  157.     buf = new byte[1024];    // allocate buffer
  158.     writeStreamHeader();
  159.     resetStream();
  160.     }
  161.  
  162.     /**
  163.      * Provide a way for subclasses that are completely reimplementing
  164.      * ObjectOutputStream to not have to allocate private data just used by
  165.      * this implementation of ObjectOutputStream.
  166.      *
  167.      * Add the following line to the security policy file to enable
  168.      * subclassing.
  169.      *
  170.      * <PRE>
  171.      *        permission SerializablePermission "enableSubclassImplementation" ;
  172.      * </PRE><p>
  173.      *
  174.      *
  175.      * @exception IOException   Thrown if not called by a subclass.
  176.      * @exception SecurityException if subclass does not have 
  177.      *                 SerializablePermission "enableSubclassImplementation".
  178.      */
  179.     protected ObjectOutputStream() throws IOException, SecurityException {
  180.     SecurityManager sm = System.getSecurityManager();
  181.     if (sm != null) sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  182.     enableSubclassImplementation = true;
  183.     }
  184.  
  185.     /**
  186.      * This method is called by trusted subclasses of ObjectInputStream
  187.      * that constructed ObjectInputStream using the 
  188.      * protected no-arg constructor. The subclass is expected to provide
  189.      * an override method with the modifier "final".
  190.      *
  191.      * @see #ObjectOutputStream()
  192.      * @see @writeObject
  193.      * @since JDK 1.2
  194.      */
  195.     protected void writeObjectOverride(Object obj) throws IOException 
  196.     {
  197.     }
  198.  
  199.     /**
  200.      * Specify stream protocol version to use when writing the stream.<p>
  201.      *
  202.      * This routine provides a hook to enable the current version
  203.      * of Serialization to write in a format that is backwards
  204.      * compatible to a previous version of the stream format.<p>
  205.      *
  206.      * Every effort will be made to avoid introducing additional 
  207.      * backwards incompatibilities; however, sometimes there is no 
  208.      * other alternative.<p>
  209.      *
  210.      * @param version   use ProtocolVersion from java.io.ObjectStreamConstants.
  211.      * @exception IllegalStateException   Thrown if called after any objects have
  212.      *                          been serialized.
  213.      * @exception IllegalArgument if invalid version is passed in.
  214.      *
  215.      * @see java.io.ObjectStreamConstants#IntialProtocolVersion
  216.      */
  217.     public void useProtocolVersion(int version) throws IOException {    
  218.     if (nextWireOffset != 0)
  219.         throw new IllegalStateException("Must call useProtocolVersion before writing any objects to the stream");
  220.  
  221.     switch (version) {
  222.     case PROTOCOL_VERSION_1:
  223.         useDeprecatedExternalizableFormat = true;
  224.         break;
  225.     case PROTOCOL_VERSION_2:
  226.         break;
  227.     default:
  228.         throw new IllegalArgumentException("unknown version:" + version);
  229.     };
  230.     }
  231.  
  232.     /**
  233.      * Write the specified object to the ObjectOutputStream.
  234.      * The class of the object, the signature of the class, and the values
  235.      * of the non-transient and non-static fields of the class and all
  236.      * of its supertypes are written.  Default serialization for a class can be
  237.      * overridden using the writeObject and the readObject methods. 
  238.      * Objects referenced by this object are written transitively so
  239.      * that a complete equivalent graph of objects can be
  240.      * reconstructed by an ObjectInputStream.  <p>
  241.      *
  242.      * Exceptions are thrown for
  243.      * problems with the OutputStream and for classes that should not be
  244.      * serialized.  All exceptions are fatal to the OutputStream, which
  245.      * is left in an indeterminate state, and it is up to the caller
  246.      * to ignore or recover the stream state.
  247.      * @exception InvalidClassException Something is wrong with a class used by
  248.      *       serialization.
  249.      * @exception NotSerializableException Some object to be serialized does not
  250.      *      implement the java.io.Serializable interface.
  251.      * @exception IOException Any exception thrown by the underlying OutputStream.
  252.      */
  253.     public final void writeObject(Object obj)
  254.     throws IOException
  255.     {
  256.     if (enableSubclassImplementation) {
  257.         writeObjectOverride(obj);
  258.         return;
  259.     }
  260.  
  261.     Object prevObject = currentObject;
  262.     ObjectStreamClass prevClassDesc = currentClassDesc;
  263.     boolean oldBlockDataMode = setBlockData(false);
  264.     recursionDepth++;
  265.  
  266.     try {
  267.         if (serializeNullAndRepeat(obj))
  268.         return;
  269.  
  270.         if (checkSpecialClasses(obj))
  271.         return;
  272.  
  273.         Object altobj = obj;
  274.  
  275.         /* Allow the class to replace the instance to be serialized. */
  276.         if (obj instanceof Replaceable)
  277.         altobj = ((Replaceable)obj).writeReplace();
  278.  
  279.         /* If the replacment is enabled, give subclasses one chance
  280.          * to substitute a new object. If one is substituted,
  281.          * recheck for null, repeated refs, and special cased classes
  282.          */
  283.         if (enableReplace) {
  284.         altobj = replaceObject(altobj);
  285.         }
  286.  
  287.         /* If the object has been replaced check that the object
  288.          * is serializable and recheck for the special cases.
  289.          */
  290.         if (obj != altobj) {
  291.         if (altobj != null && !(altobj instanceof Serializable)) {
  292.             String clname = altobj.getClass().getName();
  293.             throw new NotSerializableException(clname);
  294.         }
  295.     
  296.         // If the alternate object is already
  297.         // serialized just remember the replacement
  298.         if (serializeNullAndRepeat(altobj)) {
  299.             addReplacement(obj, altobj);
  300.             return;
  301.         }
  302.  
  303.         /* Add this to the set of replaced objects.
  304.          * This must be done before the object is
  305.          * serialized so that if the object indirectly
  306.          * refers to the original it will be redirected to
  307.          * the replacement.
  308.          *
  309.          * NB: checkSpecialClasses should only call
  310.          * serializeNullandRepeat for objects that will not
  311.          * recurse.
  312.          */
  313.         addReplacement(obj, altobj);
  314.  
  315.         if (checkSpecialClasses(altobj))
  316.             return;
  317.  
  318.         obj = altobj;
  319.         }
  320.  
  321.         if (checkSubstitutableSpecialClasses(obj))
  322.         return;
  323.         else {
  324.         /* Write out the object as itself */
  325.         outputObject(obj);
  326.         }
  327.     } catch (IOException ee) {
  328.         if (abortIOException == null) {
  329.         try {
  330.             /* Prepare to write the exception to the stream.
  331.              * End blockdatamode in case it's set
  332.              * Write the exception code
  333.              * reset the stream to forget all previous objects
  334.              * write the exception that occurred
  335.              * reset the stream again so subsequent objects won't map to
  336.              * the exception or its args.
  337.              * Continue below to rethrow the exception.
  338.              */
  339.             setBlockData(false);
  340.  
  341.             writeCode(TC_EXCEPTION);
  342.             resetStream();
  343.             setBlockData(false); //added since resetStream set to TRUE.
  344.             this.outputObject(ee); //avoid recursing with writeObject
  345.             resetStream();
  346.  
  347.             // Set the pending exception to be rethrown.
  348.             abortIOException = ee;
  349.         } catch (IOException fatal) {
  350.             /* An exception occurred while writing the original exception to
  351.              * the stream.  The original exception is not complete in
  352.              * the stream and recusion would be bad. Supercede the original
  353.              * Exception with a StreamCorruptedException using the message
  354.              * from this current exception.
  355.              */
  356.             abortIOException =
  357.             new StreamCorruptedException(fatal.getMessage());
  358.         }
  359.         }
  360.     } finally {
  361.         /* Restore state of previous call incase this is a nested call */
  362.         recursionDepth--;
  363.         currentObject = prevObject;
  364.         currentClassDesc = prevClassDesc;
  365.         setBlockData(oldBlockDataMode);
  366.     }
  367.     
  368.     /* If the recursion depth is 0, test for and clear the pending exception.
  369.      * If there is a pending exception throw it.
  370.      */
  371.     IOException pending = abortIOException;
  372.     if (recursionDepth == 0)
  373.         abortIOException = null;
  374.     if (pending != null) {
  375.         throw pending;
  376.     }
  377.     }
  378.  
  379.     /*
  380.      * Check for special cases of serializing objects. 
  381.      * These objects are not subject to replacement.
  382.      */
  383.     private boolean checkSpecialClasses(Object obj) throws IOException {
  384.  
  385.     /*
  386.      * If this is a class, don't allow substitution
  387.      */
  388.     if (obj instanceof Class) {
  389.         outputClass((Class)obj);
  390.         return true;
  391.     }
  392.  
  393.     if (obj instanceof ObjectStreamClass) {
  394.         outputClassDescriptor((ObjectStreamClass)obj);
  395.         return true;
  396.     }
  397.  
  398.     return false;
  399.     }
  400.  
  401.     /*
  402.      * Check for special cases of substitutable serializing objects.
  403.      * These classes are replaceable.
  404.      */
  405.     private boolean checkSubstitutableSpecialClasses(Object obj)
  406.     throws IOException
  407.     {
  408.     if (obj instanceof String) {
  409.         outputString((String)obj);
  410.         return true;
  411.     }
  412.  
  413.     if (obj.getClass().isArray()) {
  414.         outputArray(obj);
  415.         return true;
  416.     }
  417.  
  418.     return false;
  419.     }
  420.     
  421.     /**
  422.      * Write the non-static and non-transient fields of the current class
  423.      * to this stream.  This may only be called from the writeObject method
  424.      * of the class being serialized. It will throw the NotActiveException
  425.      * if it is called otherwise.
  426.      */
  427.     public void defaultWriteObject() throws IOException {
  428.     if (currentObject == null || currentClassDesc == null)
  429.         throw new NotActiveException("defaultWriteObject");
  430.     
  431.     ObjectStreamField[] fields = 
  432.         currentClassDesc.getFieldsNoCopy();
  433.     if (fields.length > 0) {
  434.         boolean prevmode = setBlockData(false);
  435.         outputClassFields(currentObject, currentClassDesc.forClass(),
  436.                   fields);
  437.         setBlockData(prevmode);
  438.     }
  439.     }
  440.  
  441.     /**
  442.      * Retrieve the object used to buffer persistent fields to be written to
  443.      * the stream.  The fields will be written to the stream when writeFields
  444.      * method is called.
  445.      *
  446.      * @since JDK1.2
  447.      */
  448.     public ObjectOutputStream.PutField putFields() throws IOException {
  449.     if (currentObject == null || currentClassDesc == null)
  450.         throw new NotActiveException("putFields");
  451.         // TBD: check if defaultWriteObject has already been called.
  452.         currentPutFields = new ObjectOutputStream.PutFieldImpl(currentClassDesc);
  453.         return currentPutFields;
  454.     }
  455.  
  456.     /**
  457.      * Write the buffered fields to the stream.
  458.      *
  459.      * @since JDK1.2
  460.      * @exception NotActiveException Called when a classes writeObject
  461.      * method was not called to write the state of the object.
  462.      */
  463.     public void writeFields() throws IOException {
  464.         if (currentObject == null || currentClassDesc == null || currentPutFields == null)
  465.             throw new NotActiveException("writeFields");
  466.  
  467.     boolean prevmode = setBlockData(false);
  468.         currentPutFields.write(this);
  469.     setBlockData(prevmode);
  470.     }
  471.  
  472.     /**
  473.      * Reset will disregard the state of any objects already written
  474.      * to the stream.  The state is reset to be the same as a new
  475.      * ObjectOutputStream.  The current point in the stream is marked
  476.      * as reset so the corresponding ObjectInputStream will be reset
  477.      * at the same point.  Objects previously written to the stream
  478.      * will not be refered to as already being in the stream.  They
  479.      * will be written to the stream again.
  480.      */
  481.     public void reset() throws IOException {
  482.     if (currentObject != null || currentClassDesc != null)
  483.         throw new IOException("Illegal call to reset");
  484.     
  485.     /* Write a reset to the stream. */        
  486.     setBlockData(false);
  487.     writeCode(TC_RESET);
  488.     
  489.     resetStream();            // re-init the stream
  490.     abortIOException = null;
  491.     }
  492.  
  493.     /*
  494.      * Internal reset function to reinitialize the state of the stream.
  495.      * Reset state of things changed by using the stream.
  496.      */
  497.     private void resetStream() throws IOException {
  498.     if (wireHandle2Object == null) {
  499.         wireHandle2Object = new Object[100];
  500.         wireNextHandle = new int[100];
  501.         wireHash2Handle = new int[101];
  502.     } else {
  503.  
  504.         // Storage Optimization for frequent calls to reset method.
  505.         // Do not reallocate, only reinitialize.
  506.         for (int i = 0; i < nextWireOffset; i++) {
  507.         wireHandle2Object[i] = null;
  508.         wireNextHandle[i] = 0;
  509.         }
  510.     }
  511.       nextWireOffset = 0;
  512.       for (int i = 0; i < wireHash2Handle.length; i++) {
  513.           wireHash2Handle[i] = -1;
  514.       }
  515.  
  516.     if (classDescStack == null)
  517.         classDescStack = new Stack();
  518.     else
  519.         classDescStack.setSize(0);
  520.     
  521.     for (int i = 0; i < nextReplaceOffset; i++)
  522.         replaceObjects[i] = null;
  523.     nextReplaceOffset = 0;
  524.  
  525.       setBlockData(true);        /* Re-enable buffering */
  526.       }
  527.     
  528.     /**
  529.      * Subclasses may implement this method to allow class data to be stored
  530.      * in the stream. By default this method does nothing.
  531.      * The corresponding method in ObjectInputStream is resolveClass.
  532.      * This method is called exactly once for each unique class in the stream.
  533.      * The class name and signature will have already been written to the stream.
  534.      * This method may make free use of the ObjectOutputStream to save
  535.      * any representation of the class it deems suitable (for example,
  536.      * the bytes of the class file).  The resolveClass method in the corresponding
  537.      * subclass of ObjectInputStream must read and use any data or objects
  538.      * written by annotateClass. 
  539.      *
  540.      * @exception IOException Any exception thrown by the underlying OutputStream.
  541.      */
  542.     protected void annotateClass(Class cl)
  543.     throws IOException
  544.     {
  545.     }
  546.  
  547.     /** This method will allow trusted subclasses of ObjectOutputStream
  548.      * to substitute one object for another during
  549.      * serialization. Replacing objects is disabled until
  550.      * enableReplaceObject is called. The enableReplaceObject method
  551.      * checks that the stream requesting to do replacment can be
  552.      * trusted. Every reference to serializable objects is passed to
  553.      * replaceObject.  To insure that the private state of objects is
  554.      * not unintentionally exposed only trusted streams may use
  555.      * replaceObject. <p>
  556.      *
  557.      * When a subclass is replacing objects it must insure that either
  558.      * a complementary substitution must be made during
  559.      * deserialization or that the substituted object is compatible
  560.      * with every field where the reference will be stored.  Objects
  561.      * whose type is not a subclass of the type of the field or array
  562.      * element abort the serialization by raising an exception and the
  563.      * object is not be stored. <p>
  564.      *
  565.      * This method is called only once when each object is first encountered.
  566.      * All subsequent references to the object will be redirected to the
  567.      * new object. This method should return the object to be substituted or
  568.      * the original object. <P>
  569.      *
  570.      * Null can be returned as the object to be substituted, but may
  571.      * cause NullReferenceException in classes that contain references
  572.      * to the original object since they may be expecting an object
  573.      * instead of null.<p>
  574.      *
  575.      * @exception IOException Any exception thrown by the underlying
  576.      * OutputStream.
  577.      */
  578.     protected Object replaceObject(Object obj)
  579.     throws IOException
  580.     {
  581.     return obj;
  582.     }
  583.  
  584.     /**
  585.      * Enable the stream to do replacement of objects in the stream.
  586.      * If the stream is a trusted class it is allowed to enable replacement.
  587.      * 
  588.      * 
  589.      * When enabled the replaceObject method is called for every object
  590.      * being serialized.
  591.      * 
  592.      * Add the following line to the security policy file to enable
  593.      * replaceObject() to be overriden by the subclass of ObjectOutputStream.
  594.      *
  595.      *        permission SerializablePermission "enableSubstitution" ;
  596.      *
  597.      * @exception java.security.SecurityException 
  598.      *             if SerializablePermission "enableSubstitution" 
  599.      *             is not allowed by the security policy.
  600.      */
  601.     protected boolean enableReplaceObject(boolean enable)
  602.     throws SecurityException
  603.     {
  604.     boolean previous = enableReplace;
  605.     if (enable) {
  606.         SecurityManager sm = System.getSecurityManager();
  607.         if (sm != null) sm.checkPermission(SUBSTITUTION_PERMISSION);
  608.          enableReplace = true;
  609.     } else {
  610.         enableReplace = false;
  611.     }
  612.     return previous;
  613.     }
  614.  
  615.     /**
  616.      * The writeStreamHeader method is provided so subclasses can
  617.      * append or prepend their own header to the stream.
  618.      * It writes the magic number and version to the stream.
  619.      */
  620.     protected void writeStreamHeader() throws IOException {
  621.     writeShort(STREAM_MAGIC);
  622.     writeShort(STREAM_VERSION);
  623.     }
  624.  
  625.     /**
  626.      * Write a string to the stream.
  627.      * Note that since Strings are Objects, writeObject
  628.      * will behave identically.
  629.      */
  630.     private void outputString(String s) throws IOException {
  631.     /* Allocate a write handle but don't write it to the stream,
  632.      * Write out the code for a string,
  633.      * the read can regenerate the same sequence and it saves bytes.
  634.      */
  635.     assignWireOffset(s);
  636.     writeCode(TC_STRING);
  637.     writeUTF(s); 
  638.     }
  639.  
  640.  
  641.     /* Classes are special, they can not be created during deserialization,
  642.      * but the appropriate class can be found. 
  643.      */
  644.     private void outputClass(Class aclass) throws IOException {
  645.  
  646.     writeCode(TC_CLASS);
  647.     /* Find the class descriptor and write it out */
  648.     ObjectStreamClass v = ObjectStreamClass.lookupInternal(aclass);
  649.     if (v == null)
  650.         throw new NotSerializableException(aclass.getName());
  651.  
  652.     outputClassDescriptor(v);
  653.  
  654.     assignWireOffset(aclass);
  655.     }
  656.   
  657.  
  658.     /* Write the class descriptor */
  659.     private void outputClassDescriptor(ObjectStreamClass classdesc) 
  660.     throws IOException
  661.     {
  662.     if (serializeNullAndRepeat(classdesc))
  663.         return;
  664.  
  665.     /* Write out the code for a class
  666.      * Write out the class name and its serialVersionUID
  667.      */
  668.     writeCode(TC_CLASSDESC);
  669.     String classname = classdesc.getName();
  670.  
  671.     writeUTF(classname);
  672.     writeLong(classdesc.getSerialVersionUID());
  673.  
  674.     /* This is done here to be symetric with the inputClass method
  675.      * Since the resolveClassName() method may use the stream.
  676.      * The assignments of wirehandles must be done in the same order
  677.      */
  678.     assignWireOffset(classdesc);
  679.  
  680.     /* Write the version description for this class */
  681.     classdesc.write(this);
  682.  
  683.     /* Give subclassers a chance to add the class implementation
  684.      * to the stream.  Set BlockData mode so any information they
  685.      * write can be skipped on reading.
  686.      */
  687.     boolean prevMode = setBlockData(true);
  688.     annotateClass(classdesc.forClass());
  689.     setBlockData(prevMode);
  690.     writeCode(TC_ENDBLOCKDATA);
  691.  
  692.     /*
  693.      * Write out the superclass descriptor of this descriptor
  694.      * only if it is for a java.io.Serializable class.
  695.      * else write null.
  696.      */
  697.     ObjectStreamClass superdesc = classdesc.getSuperclass();
  698.     outputClassDescriptor(superdesc);
  699.     }
  700.     
  701.     /**
  702.      * Write an array out. Note that since Arrays are Objects, writeObject(obj)
  703.      * will behave identically. <br><br>
  704.      * @param o can represent an array of any type/dimension.
  705.      */
  706.     private void outputArray(Object obj)
  707.     throws IOException
  708.     {
  709.     Class currclass = obj.getClass();
  710.  
  711.     ObjectStreamClass v = ObjectStreamClass.lookup(currclass);
  712.  
  713.     /* Write out the code for an array and the name of the class */
  714.     writeCode(TC_ARRAY);
  715.     outputClassDescriptor(v);
  716.  
  717.     /* Assign the wirehandle for this object and outputArrayValues
  718.      * writes the length and the array contents.
  719.      */
  720.     assignWireOffset(obj);
  721.  
  722.     int i, length;
  723.     Class type = currclass.getComponentType();
  724.  
  725.     if (type.isPrimitive()) {
  726.         /* Write arrays of primitive types using the DataOutput
  727.          * methods that convert each element into the output buffer.
  728.          * The data types are ordered by the frequency
  729.          * in which they are expected to occur.
  730.          */
  731.         if (type == Integer.TYPE) {
  732.         int[] array = (int[])obj;
  733.         length = array.length;
  734.         writeInt(length);
  735.         for (i = 0; i < length; i++) {
  736.             writeInt(array[i]);
  737.         }
  738.         } else if (type == Byte.TYPE) {
  739.         byte[] array = (byte[])obj;
  740.         length = array.length;
  741.         writeInt(length);
  742.         write(array, 0, length);
  743.         } else if (type == Long.TYPE) {
  744.         long[] array = (long[])obj;
  745.         length = array.length;
  746.         writeInt(length);
  747.         for (i = 0; i < length; i++) {
  748.             writeLong(array[i]);
  749.         }
  750.         } else if (type == Float.TYPE) {
  751.         float[] array = (float[])obj;
  752.         length = array.length;
  753.         writeInt(length);
  754.         for (i = 0; i < length; i++) {
  755.             writeFloat(array[i]);
  756.         }
  757.         } else if (type == Double.TYPE) {
  758.         double[] array = (double[])obj;
  759.         length = array.length;
  760.         writeInt(length);
  761.         for (i = 0; i < length; i++) {
  762.             writeDouble(array[i]);
  763.         }
  764.         } else if (type == Short.TYPE) {
  765.         short[] array = (short[])obj;
  766.         length = array.length;
  767.         writeInt(length);
  768.         for (i = 0; i < length; i++) {
  769.             writeShort(array[i]);
  770.         }
  771.         } else if (type == Character.TYPE) {
  772.         char[] array = (char[])obj;
  773.         length = array.length;
  774.         writeInt(length);
  775.         for (i = 0; i < length; i++) {
  776.             writeChar(array[i]);
  777.         }
  778.         } else if (type == Boolean.TYPE) {
  779.         boolean[] array = (boolean[])obj;
  780.         length = array.length;
  781.         writeInt(length);
  782.         for (i = 0; i < length; i++) {
  783.             writeBoolean(array[i]);
  784.         }
  785.         } else {
  786.         throw new InvalidClassException(currclass.getName());
  787.         }
  788.     } else {
  789.         Object[] array = (Object[])obj;
  790.         length = array.length;
  791.         writeInt(length);
  792.         for (i = 0; i < length; i++) {
  793.         writeObject(array[i]);
  794.         }
  795.     }
  796.     }
  797.  
  798.     /*
  799.      * Put the object into the stream The newObject code is written
  800.      * followed by the ObjectStreamClass for the object's class.  Each
  801.      * of the objects classes is written using the default
  802.      * serialization code and dispatching to Specials where
  803.      * appropriate.
  804.      */
  805.     private void outputObject(Object obj)
  806.     throws IOException
  807.     {
  808.     currentObject = obj;
  809.     Class currclass = obj.getClass();
  810.  
  811.     /* Get the Class descriptor for this class,
  812.      * Throw a NotSerializableException if there is none.
  813.      */
  814.     currentClassDesc = ObjectStreamClass.lookup(currclass);
  815.     if (currentClassDesc == null) {
  816.         throw new NotSerializableException(currclass.getName());
  817.     }
  818.  
  819.     /* Write the code to expect an instance and
  820.      * the class descriptor of the instance
  821.      */
  822.     writeCode(TC_OBJECT);
  823.     outputClassDescriptor(currentClassDesc);
  824.  
  825.     /* Assign the next wirehandle */
  826.     assignWireOffset(obj);
  827.  
  828.     /* If the object is externalizable,
  829.      * call writeExternal.
  830.      * else do Serializable processing.
  831.      */
  832.     if (currentClassDesc.isExternalizable()) {
  833.         Externalizable ext = (Externalizable)obj;
  834.         if (useDeprecatedExternalizableFormat) {
  835.  
  836.         /* JDK 1.1 external data format.
  837.          * Don't write in block data mode and no terminator tag.
  838.          */
  839.         ext.writeExternal(this);
  840.         } else {
  841.  
  842.         /* JDK 1.2 Externalizable data format writes in block data mode
  843.          * and terminates externalizable data with TAG_ENDBLOCKDATA.
  844.          */
  845.         setBlockData(true);
  846.         try {
  847.             ext.writeExternal(this);
  848.         } finally {
  849.             setBlockData(false);
  850.             writeCode(TC_ENDBLOCKDATA);
  851.         }
  852.         }
  853.     } else {
  854.  
  855.         /* The object's classes should be processed from supertype to subtype
  856.          * Push all the clases of the current object onto a stack.
  857.          * Remember the stack pointer where this set of classes is being pushed.
  858.          */
  859.         int stackMark = classDescStack.size();
  860.         try {
  861.         ObjectStreamClass next;
  862.         while ((next = currentClassDesc.getSuperclass()) != null) {
  863.             classDescStack.push(currentClassDesc);
  864.             currentClassDesc = next;
  865.         }
  866.  
  867.         /* 
  868.          * For currentClassDesc and all the pushed class descriptors
  869.          *    If the class is writing its own data
  870.          *          set blockData = true; call the class writeObject method
  871.          *    If not
  872.          *     invoke either the defaultWriteObject method.
  873.          */
  874.         do {
  875.             if (currentClassDesc.hasWriteObject()) {
  876.             setBlockData(true); /* Block any data the class writes */
  877.             invokeObjectWriter(obj, currentClassDesc.forClass());
  878.             setBlockData(false);
  879.             writeCode(TC_ENDBLOCKDATA);
  880.             } else {
  881.             defaultWriteObject();
  882.             }
  883.         } while (classDescStack.size() > stackMark &&
  884.              (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
  885.         } finally {
  886.         classDescStack.setSize(stackMark);
  887.         }
  888.     }
  889.     }
  890.  
  891.     /*
  892.      * Return a replacement for 'forObject', if one exists.
  893.      */
  894.     private Object lookupReplace(Object obj) {
  895.     for (int i = 0; i < nextReplaceOffset; i+= 2) {
  896.         if (replaceObjects[i] == obj)
  897.         return replaceObjects[i+1];
  898.     }
  899.     return obj;
  900.     }
  901.  
  902.  
  903.     /* Serialize the reference if it is NULL or is for an object that
  904.      * was already replaced or already serialized.
  905.      * If the object was already replaced, look for the replacement
  906.      * object in the known objects and if found, write its handle
  907.      * Return True if the reference is either null or a repeat.
  908.      */
  909.     private boolean serializeNullAndRepeat(Object obj)
  910.     throws IOException
  911.     {
  912.         if (obj == null) {
  913.         writeCode(TC_NULL);
  914.         return true;
  915.     }
  916.  
  917.     /* Look to see if this object has already been replaced.
  918.      * If so, proceed using the replacement object.
  919.      */
  920.     if (replaceObjects != null) {
  921.         obj = lookupReplace(obj);
  922.     }
  923.  
  924.     int handle = findWireOffset(obj);
  925.     if (handle >= 0) {
  926.         /* Add a reference to the stream */
  927.         writeCode(TC_REFERENCE);
  928.         writeInt(handle + baseWireHandle);
  929.         return true;
  930.     }
  931.     return false;        // not serialized, its up to the caller
  932.     }
  933.  
  934.     /*
  935.      * Locate and return if found the handle for the specified object.
  936.      * -1 is returned if the object does not occur in the array of
  937.      * known objects. 
  938.      */
  939.     private int findWireOffset(Object obj) {
  940.     int hash = System.identityHashCode(obj);
  941.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  942.  
  943.     for (int handle = wireHash2Handle[index];
  944.          handle >= 0;
  945.          handle = wireNextHandle[handle]) {
  946.         
  947.         if (wireHandle2Object[handle] == obj)
  948.         return handle;
  949.     }
  950.     return -1;
  951.     }
  952.  
  953.     /* Allocate a handle for an object.
  954.      * The Vector is indexed by the wireHandleOffset
  955.      * and contains the object.
  956.      */
  957.     private void assignWireOffset(Object obj)
  958.     throws IOException
  959.     {
  960.     // Extend the array if there isn't room for this new element
  961.     if (nextWireOffset == wireHandle2Object.length) {
  962.         Object[] oldhandles = wireHandle2Object;
  963.         wireHandle2Object = new Object[nextWireOffset*2];
  964.         System.arraycopy(oldhandles, 0,
  965.                  wireHandle2Object, 0,
  966.                  nextWireOffset);
  967.         int[] oldnexthandles = wireNextHandle;
  968.         wireNextHandle = new int[nextWireOffset*2];
  969.         System.arraycopy(oldnexthandles, 0,
  970.                  wireNextHandle, 0,
  971.                  nextWireOffset);
  972.         // TBD: Rehash the hash array if necessary
  973.     }
  974.     wireHandle2Object[nextWireOffset] = obj;
  975.  
  976.     hashInsert(obj, nextWireOffset);
  977.  
  978.     nextWireOffset++;
  979.     return;
  980.     }
  981.  
  982.  
  983.     /*
  984.      * Insert the specified object into the hash array and link if
  985.      * necessary. Put the new object into the hash table and link the
  986.      * previous to it. Newer objects occur earlier in the list.
  987.      */
  988.     private void hashInsert(Object obj, int offset) {
  989.     int hash = System.identityHashCode(obj);
  990.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  991.     wireNextHandle[offset] = wireHash2Handle[index];
  992.     wireHash2Handle[index] = offset;
  993.     }
  994.  
  995.     /*
  996.      * Add a replacement object to the table.
  997.      * The even numbered indices are the original objects.
  998.      * The odd numbered indices are the replacement objects.
  999.      *
  1000.      */
  1001.     private void addReplacement(Object orig, Object replacement) {
  1002.     // Extend the array if there isn't room for this new element
  1003.  
  1004.     if (replaceObjects == null) {
  1005.         replaceObjects = new Object[10];
  1006.     }
  1007.     if (nextReplaceOffset == replaceObjects.length) {
  1008.         Object[] oldhandles = replaceObjects;
  1009.         replaceObjects = new Object[2+nextReplaceOffset*2];
  1010.         System.arraycopy(oldhandles, 0,
  1011.                  replaceObjects, 0,
  1012.                  nextReplaceOffset);
  1013.     }
  1014.     replaceObjects[nextReplaceOffset++] = orig;
  1015.     replaceObjects[nextReplaceOffset++] = replacement;
  1016.     }
  1017.  
  1018.     /* Write out the code indicating the type what follows.
  1019.      * See ObjectStreamConstants for definitions.
  1020.      */
  1021.     private void writeCode(int tag)
  1022.     throws IOException
  1023.     {
  1024.     writeByte(tag);
  1025.     }
  1026.  
  1027.     /*
  1028.      * Implement the OutputStream methods.  The stream has
  1029.      * two modes used internally to ObjectOutputStream.  When
  1030.      * in BlockData mode, all writes are buffered and written
  1031.      * to the underlying stream prefixed by a code and length.
  1032.      * When not in BlockData mode (false), writes pass directly
  1033.      * through to the underlying stream.
  1034.      * The BlockData mode is used to encapsulate data written
  1035.      * by class specific writeObject methods that is intended
  1036.      * only to be read by corresponding readObject method of the 
  1037.      * same class.  The blocking of data allows it to be skipped
  1038.      * if necessary.
  1039.      *
  1040.      * The setBlockData method is used to switch buffering
  1041.      * on and off.  When switching between on and off
  1042.      * the buffer is drained.
  1043.      *
  1044.      * The actual buffering is very similar to that of
  1045.      * BufferedOutputStream but BufferedOutputStream can
  1046.      * write to the underlying stream without the headers.
  1047.      */
  1048.     private boolean blockDataMode;    /* true to buffer and block data */
  1049.     private byte[] buf;        /* byte array of buffered data. */
  1050.     private int count;        /* count of bytes in the buffer */
  1051.     private OutputStream out;    /* Stream to write the data to */
  1052.  
  1053.     /**
  1054.      * Writes a byte. This method will block until the byte is actually
  1055.      * written.
  1056.      * @param b    the byte
  1057.      * @exception IOException If an I/O error has occurred.
  1058.      */
  1059.     public void write(int data) throws IOException {
  1060.     
  1061.     if (count >= buf.length)
  1062.         drain();        /* Drain, make room for more */
  1063.     buf[count++] = (byte)data;
  1064.     }
  1065.  
  1066.     /**
  1067.      * Writes an array of bytes. This method will block until the bytes
  1068.      * are actually written.
  1069.      * @param b    the data to be written
  1070.      * @exception IOException If an I/O error has occurred.
  1071.      */
  1072.     public void write(byte b[]) throws IOException {
  1073.     write(b, 0, b.length);
  1074.     }
  1075.  
  1076.     /**
  1077.      * Writes a sub array of bytes. 
  1078.      * @param b    the data to be written
  1079.      * @param off    the start offset in the data
  1080.      * @param len    the number of bytes that are written
  1081.      * @exception IOException If an I/O error has occurred.
  1082.      */
  1083.     public void write(byte b[], int off, int len) throws IOException {
  1084.     if (len < 0)
  1085.         throw new IndexOutOfBoundsException();
  1086.  
  1087.     if (blockDataMode) {
  1088.         writeCanonical(b, off, len);
  1089.     } else {
  1090.         /*
  1091.          * If array will fit in output buffer, copy it in there; otherwise,
  1092.          * drain anything in the buffer and send it through to underlying
  1093.          * output stream directly.
  1094.          */
  1095.         int avail = buf.length - count;
  1096.         if (len <= avail) {
  1097.         System.arraycopy(b, off, buf, count, len);
  1098.         count += len;
  1099.         } else {
  1100.         drain();
  1101.         out.write(b, off, len);
  1102.         }
  1103.     }
  1104.     }
  1105.  
  1106.     /**
  1107.      * Flushes the stream. This will write any buffered
  1108.      * output bytes and flush through to the underlying stream.
  1109.      * @exception IOException If an I/O error has occurred.
  1110.      */
  1111.     public void flush() throws IOException {
  1112.     drain();
  1113.     out.flush();
  1114.     }
  1115.  
  1116.     /**
  1117.      * Drain any buffered data in ObjectOutputStream.  Similar to flush
  1118.      * but does not propagate the flush to the underlaying stream.
  1119.      */
  1120.     protected void drain() throws IOException {
  1121.     /*
  1122.      * Drain the data buffer.
  1123.      * If the blocking mode is on, prepend the buffer
  1124.      * with the code for a blocked data and the length.
  1125.      * The code is TC_BLOCKDATA and the length is < 255 so it fits
  1126.      * in a byte. 
  1127.      */
  1128.     if (count == 0)
  1129.         return;
  1130.  
  1131.     if (blockDataMode)
  1132.         writeBlockDataHeader(count);
  1133.     out.write(buf, 0, count);
  1134.     count = 0;
  1135.     }
  1136.  
  1137.     /**
  1138.      * Closes the stream. This method must be called
  1139.      * to release any resources associated with the
  1140.      * stream.
  1141.      * @exception IOException If an I/O error has occurred.
  1142.      */
  1143.     public void close() throws IOException {
  1144.     flush();        /* Make sure we're not holding any data */
  1145.     out.close();
  1146.     }
  1147.  
  1148.     /*
  1149.      * Set the blockData mode,  if it turned from on to off
  1150.      * the buffer is drained.  The previous mode is returned.
  1151.      */
  1152.     private boolean setBlockData(boolean mode) throws IOException {
  1153.     if (blockDataMode == mode)
  1154.         return mode;
  1155.     drain();
  1156.     blockDataMode = mode;
  1157.     return !mode;        /* previous value was the opposite */
  1158.     }
  1159.     
  1160.     /* Write the Block-data marker and the length of the data to follow
  1161.      * to stream 'out'. If the length is < 256, use the short header form.
  1162.      * othewise use the long form.
  1163.      */
  1164.     private void writeBlockDataHeader(int len) throws IOException {
  1165.     if (len <= 255) {
  1166.         out.write(TC_BLOCKDATA);
  1167.         out.write((byte)len);
  1168.     } else {
  1169.         // use block data with int size if necessary
  1170.         out.write(TC_BLOCKDATALONG);
  1171.         // send 32 bit int directly to underlying stream
  1172.         out.write((byte)((len >> 24) & 0xFF));
  1173.         out.write((byte)((len >> 16) & 0xFF));
  1174.         out.write((byte)((len >>  8) & 0xFF));
  1175.         out.write((byte)(len & 0xFF));
  1176.     }
  1177.     }
  1178.  
  1179.     /* Canonical form requires constant blocking factor. Write data
  1180.      * in b array in constant block-data chunks.
  1181.      *
  1182.      * Assumes only called when blockDataMode is true.
  1183.      */
  1184.     private void writeCanonical(byte b[], int off, int len) 
  1185.     throws IOException
  1186.     {
  1187.     int bufAvail = buf.length - count;
  1188.     int bytesToWrite = len;
  1189.     
  1190.     // Handle case where byte array is larger than available buffer.
  1191.     if (bytesToWrite > bufAvail) {
  1192.         // Logically: fill rest of 'buf' with 'b' and drain.
  1193.         // Optimization: avoid copying to 'buf', write partial 'buf' and partial
  1194.         //               'b' directly to 'out'.
  1195.         writeBlockDataHeader(buf.length);
  1196.         out.write(buf, 0, count);
  1197.         out.write(b, off, bufAvail);
  1198.  
  1199.         count = 0;
  1200.         off += bufAvail;
  1201.         bytesToWrite -= bufAvail;
  1202.  
  1203.         // Optimization: write 'buf.length' BlockData directly to stream.
  1204.         while (bytesToWrite >= buf.length) {
  1205.         if (blockDataMode)
  1206.             writeBlockDataHeader(buf.length);
  1207.         out.write(b, off, buf.length);
  1208.         off += buf.length;
  1209.         bytesToWrite -= buf.length;
  1210.         }
  1211.     }
  1212.  
  1213.     // Put remainder of byte array into buffer. 
  1214.     if (bytesToWrite != 0) {
  1215.         System.arraycopy(b, off, buf, count, bytesToWrite);
  1216.         count += bytesToWrite;
  1217.     }
  1218.     }
  1219.     
  1220.     /* -------------------------------------------------------------- */
  1221.     /*
  1222.      * Provide the methods to implement DataOutput.
  1223.      * These are copied from DataOutputStream to avoid the overhead
  1224.      * of multiple method calls and to buffer the data directly.
  1225.      */
  1226.     private DataOutputStream dos;
  1227.  
  1228.     /**
  1229.      * Writes a boolean.
  1230.      * @param data the boolean to be written
  1231.      */
  1232.     public void writeBoolean(boolean data) throws IOException {
  1233.     if (count >= buf.length)
  1234.         drain();
  1235.     buf[count++] = (byte)(data ? 1 : 0);
  1236.  
  1237.     }
  1238.  
  1239.     /**
  1240.      * Writes an 8 bit byte.
  1241.      * @param data the byte value to be written
  1242.      */
  1243.     public void writeByte(int data) throws IOException  {
  1244.     if (count >= buf.length)
  1245.         drain();
  1246.     buf[count++] = (byte)data;
  1247.     }
  1248.  
  1249.     /**
  1250.      * Writes a 16 bit short.
  1251.      * @param data the short value to be written
  1252.      */
  1253.     public void writeShort(int data)  throws IOException {
  1254.     if (count + 2 > buf.length) {
  1255.         if (blockDataMode) {
  1256.  
  1257.         // normalize block-data record by writing a byte at a time.
  1258.         dos.writeShort(data);
  1259.         return;
  1260.         } else {
  1261.         drain();
  1262.         }
  1263.     }
  1264.     buf[count++] = (byte)((data >>>  8));
  1265.     buf[count++] = (byte)((data >>>  0));
  1266.     }
  1267.  
  1268.     /**
  1269.      * Writes a 16 bit char.
  1270.      * @param data the char value to be written
  1271.      */
  1272.     public void writeChar(int data)  throws IOException {
  1273.     if (count + 2 > buf.length) {
  1274.         if (blockDataMode) {
  1275.  
  1276.         // normalize block-data record by writing a byte at a time.
  1277.         dos.writeChar(data);
  1278.         return;
  1279.         } else {
  1280.         drain();
  1281.         }
  1282.     }
  1283.     buf[count++] = (byte)((data >>>  8));
  1284.     buf[count++] = (byte)((data >>>  0));
  1285.     }
  1286.  
  1287.     /**
  1288.      * Writes a 32 bit int.
  1289.      * @param data the integer value to be written
  1290.      */
  1291.     public void writeInt(int data)  throws IOException {
  1292.     if (count + 4 > buf.length) {
  1293.         if (blockDataMode) {
  1294.  
  1295.         // normalize block-data record by writing a byte at a time.
  1296.         dos.writeInt(data);
  1297.         return;
  1298.         } else {
  1299.         drain();
  1300.         }
  1301.     }
  1302.     buf[count++] = (byte)((data >>> 24));
  1303.     buf[count++] = (byte)((data >>> 16));
  1304.     buf[count++] = (byte)((data >>>  8));
  1305.     buf[count++] = (byte)((data >>>  0));
  1306.     }
  1307.  
  1308.     /**
  1309.      * Writes a 64 bit long.
  1310.      * @param data the long value to be written
  1311.      */
  1312.     public void writeLong(long data)  throws IOException {
  1313.     if (count + 8 > buf.length) {
  1314.         if (blockDataMode) {
  1315.  
  1316.         // normalize block-data record by writing a byte at a time.
  1317.         dos.writeLong(data);
  1318.         return;
  1319.         } else {
  1320.         drain();
  1321.         }
  1322.     }
  1323.     buf[count++] = (byte)((int)(data >>> 56));
  1324.     buf[count++] = (byte)((int)(data >>> 48));
  1325.     buf[count++] = (byte)((int)(data >>> 40));
  1326.     buf[count++] = (byte)((int)(data >>> 32));
  1327.     buf[count++] = (byte)((data >>> 24));
  1328.     buf[count++] = (byte)((data >>> 16));
  1329.     buf[count++] = (byte)((data >>>  8));
  1330.     buf[count++] = (byte)((data >>>  0));
  1331.     }
  1332.  
  1333.     /**
  1334.      * Writes a 32 bit float.
  1335.      * @param data the float value to be written
  1336.      */
  1337.     public void writeFloat(float data) throws IOException {
  1338.     int value = Float.floatToIntBits(data);
  1339.     if (count + 4 > buf.length) {
  1340.         if (blockDataMode) {
  1341.  
  1342.         // normalize block-data record by writing a byte at a time.
  1343.         dos.writeFloat(data);
  1344.         return;
  1345.         } else {
  1346.         drain();
  1347.         }
  1348.     }
  1349.     buf[count++] = (byte)((value >>> 24));
  1350.     buf[count++] = (byte)((value >>> 16));
  1351.     buf[count++] = (byte)((value >>>  8));
  1352.     buf[count++] = (byte)((value >>>  0));
  1353.     }
  1354.  
  1355.     /**
  1356.      * Writes a 64 bit double.
  1357.      * @param data the double value to be written
  1358.      */
  1359.     public void writeDouble(double data) throws IOException {
  1360.     long value = Double.doubleToLongBits(data);
  1361.     if (count + 8 > buf.length) {
  1362.         if (blockDataMode) {
  1363.  
  1364.         // normalize block-data record by writing a byte at a time.
  1365.         dos.writeDouble(data);
  1366.         return;
  1367.         } else {
  1368.         drain();
  1369.         }
  1370.     }
  1371.     buf[count++] = (byte)((int)(value >>> 56));
  1372.     buf[count++] = (byte)((int)(value >>> 48));
  1373.     buf[count++] = (byte)((int)(value >>> 40));
  1374.     buf[count++] = (byte)((int)(value >>> 32));
  1375.     buf[count++] = (byte)((value >>> 24));
  1376.     buf[count++] = (byte)((value >>> 16));
  1377.     buf[count++] = (byte)((value >>>  8));
  1378.     buf[count++] = (byte)((value >>>  0));
  1379.     }
  1380.  
  1381.     /**
  1382.      * Writes a String as a sequence of bytes.
  1383.      * @param s the String of bytes to be written
  1384.      */
  1385.     public void writeBytes(String data) throws IOException {
  1386.     dos.writeBytes(data);
  1387.     }
  1388.  
  1389.     /**
  1390.      * Writes a String as a sequence of chars.
  1391.      * @param s the String of chars to be written
  1392.      */
  1393.     public void writeChars(String data) throws IOException {
  1394.     dos.writeChars(data);
  1395.     }
  1396.  
  1397.     /**
  1398.      * Writes a String in UTF format.
  1399.      * @param str the String in UTF format
  1400.      */
  1401.     public void writeUTF(String data) throws IOException {
  1402.     dos.writeUTF(data);
  1403.     }
  1404.     
  1405.     /* Write the fields of the specified class by invoking the appropriate
  1406.      * write* method on this class.
  1407.      */
  1408.     private void outputClassFields(Object o, Class cl,
  1409.                    ObjectStreamField[] fields)
  1410.     throws IOException, InvalidClassException {
  1411.  
  1412.     for (int i = 0; i < fields.length; i++) {
  1413.         if (fields[i].getField() == null)
  1414.         throw new InvalidClassException(cl.getName(),
  1415.             "Nonexistent field " + fields[i].getName());
  1416.         
  1417.         try {
  1418.         switch (fields[i].getTypeCode()) {
  1419.         case 'B':
  1420.             byte byteValue = fields[i].getField().getByte(o);
  1421.             writeByte(byteValue);
  1422.                 break;
  1423.         case 'C':
  1424.             char charValue = fields[i].getField().getChar(o);
  1425.             writeChar(charValue);
  1426.                 break;
  1427.         case 'F': 
  1428.             float floatValue = fields[i].getField().getFloat(o);
  1429.             writeFloat(floatValue);
  1430.                 break;
  1431.         case 'D' :
  1432.             double doubleValue = fields[i].getField().getDouble(o);
  1433.             writeDouble(doubleValue);
  1434.             break;
  1435.         case 'I':
  1436.             int intValue = fields[i].getField().getInt(o);
  1437.             writeInt(intValue);
  1438.                 break;
  1439.         case 'J':
  1440.             long longValue = fields[i].getField().getLong(o);
  1441.             writeLong(longValue);
  1442.             break;
  1443.         case 'S': 
  1444.             short shortValue = fields[i].getField().getShort(o);
  1445.             writeShort(shortValue);
  1446.                 break;
  1447.         case 'Z':
  1448.             boolean booleanValue = fields[i].getField().getBoolean(o);
  1449.             writeBoolean(booleanValue);
  1450.                 break;
  1451.         case '[':
  1452.         case 'L':
  1453.             Object objectValue = fields[i].getField().get(o);
  1454.             writeObject(objectValue);
  1455.                 break;
  1456.         default:
  1457.             throw new InvalidClassException(cl.getName());
  1458.         }
  1459.         } catch (IllegalAccessException e) {
  1460.         throw new InvalidClassException(cl.getName(), e.getMessage());
  1461.         }
  1462.     }
  1463.     }
  1464.  
  1465.     /*
  1466.      * Test if WriteObject method is present, and if so, invoke writer.
  1467.      */
  1468.     private void invokeObjectWriter(Object obj, Class c)
  1469.     throws IOException
  1470.     {
  1471.     try {
  1472.         AccessController.beginPrivileged();
  1473.         currentClassDesc.writeObjectMethod.invoke(obj, writeObjectArglist);
  1474.     } catch (InvocationTargetException e) {
  1475.         Throwable t = e.getTargetException();
  1476.         if (t instanceof IOException)
  1477.         throw (IOException)t;
  1478.         else if (t instanceof RuntimeException)
  1479.         throw (RuntimeException) t;
  1480.         else if (t instanceof Error)
  1481.         throw (Error) t;
  1482.         else
  1483.         throw new Error("interal error");
  1484.     } catch (IllegalAccessException e) {
  1485.         // cannot happen
  1486.     } finally {
  1487.         AccessController.endPrivileged();
  1488.     }
  1489.     }
  1490.  
  1491.     /*************************************/
  1492.  
  1493.     /**
  1494.      * Provide programatic access to the persistent fields to be written 
  1495.      * to ObjectOutput.
  1496.      *
  1497.      * @since JDK 1.2
  1498.      */
  1499.     static public abstract class PutField {
  1500.     /**
  1501.      * Put the value of the named boolean field into the persistent field.
  1502.      */
  1503.     abstract public void put(String name, boolean value);
  1504.  
  1505.     /**
  1506.      * Put the value of the named char field into the persistent fields.
  1507.      */
  1508.     abstract public void put(String name, char value);
  1509.  
  1510.     /**
  1511.      * Put the value of the named byte field into the persistent fields.
  1512.      */
  1513.     abstract public void put(String name, byte value);
  1514.     
  1515.     /**
  1516.      * Put the value of the named short field into the persistent fields.
  1517.      */
  1518.     abstract public void put(String name, short value);
  1519.  
  1520.     /**
  1521.      * Put the value of the named int field into the persistent fields.
  1522.      */
  1523.     abstract public void put(String name, int value);
  1524.  
  1525.     /**
  1526.      * Put the value of the named long field into the persistent fields.
  1527.      */
  1528.     abstract public void put(String name, long value);
  1529.  
  1530.     /**
  1531.      * Put the value of the named float field into the persistent fields.
  1532.      */
  1533.     abstract public void put(String name, float value);
  1534.  
  1535.     /**
  1536.      * Put the value of the named double field into the persistent field.
  1537.      */
  1538.     abstract public void put(String name, double value);
  1539.  
  1540.     /**
  1541.      * Put the value of the named Object field into the persistent field.
  1542.      */
  1543.     abstract public void put(String name, Object value);
  1544.     
  1545.     /**
  1546.      * Write the data and fields to the specified ObjectOutput stream.
  1547.      */
  1548.     abstract public void write(ObjectOutput out) throws IOException;
  1549.     };
  1550.  
  1551.     /*************************************************************/
  1552.  
  1553.  
  1554.     /**
  1555.      * Provide access to the persistent fields to be written to the output stream.
  1556.      */
  1557.     static class PutFieldImpl extends PutField {
  1558.      /**
  1559.       * Put the value of the named boolean field into the persistent field.
  1560.       */
  1561.      public void put(String name, boolean value)
  1562.          throws IllegalArgumentException
  1563.      {
  1564.          ObjectStreamField field = desc.getField(name);
  1565.          if (field == null || field.getType() != Boolean.TYPE)
  1566.          throw new IllegalArgumentException("No such boolean field");
  1567.          data[field.getOffset()] = (byte)(value ? 1 : 0);
  1568.      }
  1569.  
  1570.      /**
  1571.       * Put the value of the named char field into the persistent fields.
  1572.       */
  1573.      public void put(String name, char value) {
  1574.          ObjectStreamField field = desc.getField(name);;
  1575.          if (field == null || field.getType() != Character.TYPE)
  1576.          throw new IllegalArgumentException("No such char field");
  1577.          data[field.getOffset()] = (byte)(value >> 8);
  1578.          data[field.getOffset()+1] = (byte)(value);
  1579.      }
  1580.  
  1581.      /**
  1582.       * Put the value of the named byte field into the persistent fields.
  1583.       */
  1584.      public void put(String name, byte value) {
  1585.          ObjectStreamField field = desc.getField(name);;
  1586.          if (field == null || field.getType() != Byte.TYPE)
  1587.          throw new IllegalArgumentException("No such byte field");
  1588.          data[field.getOffset()] = value;
  1589.      }
  1590.  
  1591.      /**
  1592.       * Put the value of the named short field into the persistent fields.
  1593.       */
  1594.      public void put(String name, short value) {
  1595.          ObjectStreamField field = desc.getField(name);;
  1596.          if (field == null || field.getType() != Short.TYPE)
  1597.          throw new IllegalArgumentException("No such short field");
  1598.  
  1599.          int loffset = field.getOffset();
  1600.          data[loffset] = (byte)(value >> 8);
  1601.          data[loffset+1] = (byte)(value);
  1602.      }
  1603.  
  1604.      /**
  1605.       * Put the value of the named int field into the persistent fields.
  1606.       */
  1607.      public void put(String name, int value) {
  1608.          ObjectStreamField field = desc.getField(name);
  1609.          if (field == null || field.getType() != Integer.TYPE)
  1610.          throw new IllegalArgumentException("No such int field");
  1611.  
  1612.          int loffset = field.getOffset();
  1613.          data[loffset] = (byte)(value >> 24);
  1614.          data[loffset+1] = (byte)(value >> 16);
  1615.          data[loffset+2] = (byte)(value >> 8);
  1616.          data[loffset+3] = (byte)value;
  1617.      }
  1618.  
  1619.      /**
  1620.       * Put the value of the named long field into the persistent fields.
  1621.       */
  1622.      public void put(String name, long value) {
  1623.          ObjectStreamField field = desc.getField(name);
  1624.          if (field == null || field.getType() != Long.TYPE)
  1625.          throw new IllegalArgumentException("No such long field");
  1626.  
  1627.          int loffset = field.getOffset();
  1628.          data[loffset] = (byte)(value >> 56);
  1629.          data[loffset+1] = (byte)(value >> 48);
  1630.          data[loffset+2] = (byte)(value >> 40);
  1631.          data[loffset+3] = (byte)(value >> 32);
  1632.          data[loffset+4] = (byte)(value >> 24);
  1633.          data[loffset+5] = (byte)(value >> 16);
  1634.          data[loffset+6] = (byte)(value >> 8);
  1635.          data[loffset+7] = (byte)value;
  1636.      }
  1637.  
  1638.      /**
  1639.       * Put the value of the named float field into the persistent fields.
  1640.       */
  1641.      public void put(String name, float value) {
  1642.          int val = Float.floatToIntBits(value);
  1643.          ObjectStreamField field = desc.getField(name);
  1644.          if (field == null || field.getType() != Float.TYPE)
  1645.          throw new IllegalArgumentException("No such float field");
  1646.  
  1647.          int loffset = field.getOffset();
  1648.          data[loffset] = (byte)(val >> 24);
  1649.          data[loffset+1] = (byte)(val >> 16);
  1650.          data[loffset+2] = (byte)(val >> 8);
  1651.          data[loffset+3] = (byte)val;
  1652.      }
  1653.  
  1654.      /**
  1655.          * Put the value of the named double field into the persistent field.
  1656.          */
  1657.      public void put(String name, double value) {
  1658.          long val = Double.doubleToLongBits(value);
  1659.          ObjectStreamField field = desc.getField(name);
  1660.          if (field == null || field.getType() != Double.TYPE)
  1661.          throw new IllegalArgumentException("No such double field");
  1662.  
  1663.          int loffset = field.getOffset();
  1664.          data[loffset] = (byte)(val >> 56);
  1665.          data[loffset+1] = (byte)(val >> 48);
  1666.          data[loffset+2] = (byte)(val >> 40);
  1667.          data[loffset+3] = (byte)(val >> 32);
  1668.          data[loffset+4] = (byte)(val >> 24);
  1669.          data[loffset+5] = (byte)(val >> 16);
  1670.          data[loffset+6] = (byte)(val >> 8);
  1671.          data[loffset+7] = (byte)val;
  1672.      }
  1673.  
  1674.      /**
  1675.       * Put the value of the named Object field into the persistent field.
  1676.       */
  1677.      public void put(String name, Object value) {
  1678.          ObjectStreamField field = desc.getField(name);
  1679.          if (field == null || field.isPrimitive())
  1680.          throw new IllegalArgumentException("No such object field");
  1681.          objects[field.getOffset()] = value;
  1682.      }
  1683.  
  1684.      /**
  1685.       * Write the data and fields to the specified stream.
  1686.       */
  1687.      public void write(ObjectOutput out) throws IOException {
  1688.          if (data != null)
  1689.          out.write(data, 0, data.length);
  1690.  
  1691.          if (objects != null) {
  1692.          for (int i = 0; i < objects.length; i++)
  1693.              out.writeObject(objects[i]);
  1694.          }
  1695.      }
  1696.  
  1697.      /**
  1698.       * Create a PutField object for the a Class.
  1699.       * Allocate the arrays for primitives and objects.
  1700.       */
  1701.      PutFieldImpl(ObjectStreamClass descriptor) {
  1702.          desc = descriptor;
  1703.          if (desc.primBytes > 0)
  1704.          data = new byte[desc.primBytes];
  1705.          if (desc.objFields > 0)
  1706.          objects = new Object[desc.objFields];
  1707.      }
  1708.  
  1709.     /*
  1710.      * The byte array that contains the bytes for the primitive fields.
  1711.      * The Object array that contains the objects for the object fields.
  1712.      */
  1713.      private byte[] data;
  1714.      private Object[] objects;
  1715.      private ObjectStreamClass desc;
  1716.      };
  1717.  
  1718.     /*************************************************************/
  1719.     
  1720.     /* Remember the first exception that stopped this stream. */
  1721.     private IOException abortIOException = null;
  1722.     
  1723.     /* Object references are mapped to the wire handles through a hashtable
  1724.      * WireHandles are integers generated by the ObjectOutputStream,
  1725.      * they need only be unique within a stream.
  1726.      * Objects are assigned sequential handles and stored in wireHandle2Object.
  1727.      * The handle for an object is its index in wireHandle2Object.
  1728.      * Object with the "same" hashcode are chained using wireHash2Handle.
  1729.      * The hashcode of objects is used to index through the wireHash2Handle.
  1730.      * -1 is the marker for unused cells in wireNextHandle
  1731.      */
  1732.     private Object[] wireHandle2Object;
  1733.     private int[] wireNextHandle;
  1734.     private int[] wireHash2Handle;
  1735.     private int nextWireOffset;
  1736.  
  1737.     /* The object is the current object and ClassDescriptor is the current
  1738.      * subclass of the object being written. Nesting information is kept
  1739.      * on the stack.
  1740.      */
  1741.     private Object currentObject;
  1742.     private ObjectStreamClass currentClassDesc;
  1743.     private Stack classDescStack;
  1744.     private PutField currentPutFields;
  1745.     private Object[] writeObjectArglist = {this};
  1746.  
  1747.     /* 
  1748.      * Flag set to true to allow replaceObject to be called.
  1749.      * Set by enableReplaceObject.
  1750.      * The array of replaceObjects and the index of the next insertion.
  1751.      */
  1752.     boolean enableReplace;
  1753.     private Object[] replaceObjects;
  1754.     private int nextReplaceOffset;
  1755.  
  1756.     /* Recursion level, starts at zero and is incremented for each entry
  1757.      * to writeObject.  Decremented before exit.
  1758.      */ 
  1759.     private int recursionDepth = 0;
  1760.  
  1761.     /* If true, use JDK 1.1 Externalizable data format. */
  1762.     boolean useDeprecatedExternalizableFormat = false;
  1763.  
  1764.     /* if true, override writeObject implementation with writeObjectOverride.
  1765.      */
  1766.     private final boolean enableSubclassImplementation;
  1767. }
  1768.