home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 July & August / Pcwk78a98.iso / Internet / Javadraw / DATA.Z / ObjectStreamClass.java < prev    next >
Text File  |  1997-08-30  |  22KB  |  746 lines

  1. /*
  2.  * @(#)ObjectStreamClass.java    1.30 97/01/27
  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 sun.misc.Ref;
  25. import java.security.MessageDigest;
  26. import java.security.NoSuchAlgorithmException;
  27. import java.security.DigestOutputStream;
  28. import java.lang.reflect.Modifier;
  29.  
  30. /**
  31.  * A ObjectStreamClass describes a class that can be serialized to a stream
  32.  * or a class that was serialized to a stream.  It contains the name
  33.  * and the serialVersionUID of the class.
  34.  * <br>
  35.  * The ObjectStreamClass for a specific class loaded in this Java VM can
  36.  * be found using the lookup method.
  37.  *
  38.  * @author  unascribed
  39.  * @version 1.30, 01/27/97
  40.  * @since   JDK1.1
  41.  */
  42. public class ObjectStreamClass implements java.io.Serializable {
  43.     /** Find the descriptor for a class that can be serialized.  Null
  44.      * is returned if the specified class does not implement
  45.      * java.io.Serializable or java.io.Externalizable.
  46.      * @since   JDK1.1
  47.      */
  48.     public static ObjectStreamClass lookup(Class cl)
  49.     {
  50.     /* Synchronize on the hashtable so no two threads will do
  51.      * this at the same time.
  52.      */
  53.     ObjectStreamClass v = null;
  54.     synchronized (descriptorFor) {
  55.         /* Find the matching descriptor if it already known */
  56.         v = findDescriptorFor(cl);
  57.         if (v != null) {
  58.         return v;
  59.         }
  60.         
  61.         /* Check if it's serializable or externalizable.
  62.          * Since Externalizable extends Serializiable, return
  63.          * null immediately if it's not Serializable.
  64.          */
  65.         boolean serializable = classSerializable.isAssignableFrom(cl);
  66.         if (!serializable)
  67.         return null;
  68.  
  69.         /* Test if it's Externalizable, clear the serializable flag
  70.          * only one or the other may be set in the protocol.
  71.          */
  72.         boolean externalizable = classExternalizable.isAssignableFrom(cl);
  73.         if (externalizable)
  74.         serializable = false;
  75.  
  76.         /* If the class is only Serializable,
  77.          * lookup the descriptor for the superclass.
  78.          */
  79.         ObjectStreamClass superdesc = null;
  80.         if (serializable) {
  81.         Class superclass = cl.getSuperclass();
  82.         if (superclass != null) 
  83.             superdesc = lookup(superclass);
  84.         }
  85.  
  86.         /* Create a new version descriptor,
  87.          * it put itself in the known table.
  88.          */
  89.         v = new ObjectStreamClass(cl, superdesc,
  90.                       serializable, externalizable);
  91.     }
  92.     return v;
  93.     }
  94.     
  95.     /**
  96.      * The name of the class described by this descriptor.
  97.      * @since   JDK1.1
  98.      */
  99.     public String getName() {
  100.     return name;
  101.     }
  102.  
  103.     /**
  104.      * Return the serialVersionUID for this class.
  105.      * The serialVersionUID defines a set of classes all with the same name
  106.      * that have evolved from a common root class and agree to be serialized
  107.      * and deserialized using a common format.
  108.      * @since   JDK1.1
  109.      */
  110.     public long getSerialVersionUID() {
  111.     return suid;
  112.     }
  113.  
  114.     /**
  115.      * Return the class in the local VM that this version is mapped to.
  116.      * Null is returned if there is no corresponding local class.
  117.      * @since   JDK1.1
  118.      */
  119.     public Class forClass() {
  120.     return ofClass;
  121.     }
  122.     
  123.     /**
  124.      * Return a string describing this ObjectStreamClass.
  125.      * @since   JDK1.1
  126.      */
  127.     public String toString() {
  128.     StringBuffer sb = new StringBuffer();
  129.  
  130.     sb.append(name);
  131.     sb.append(": static final long serialVersionUID = ");
  132.     sb.append(Long.toString(suid));
  133.     sb.append("L;");
  134.     return sb.toString();
  135.     }
  136.  
  137.     /*
  138.      * Create a new ObjectStreamClass from a loaded class.
  139.      * Don't call this directly, call lookup instead.
  140.      */
  141.     private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc,
  142.                   boolean serial, boolean extern)
  143.     {
  144.     int i;
  145.     ofClass = cl;        /* created from this class */
  146.  
  147.     name = cl.getName();
  148.     superclass = superdesc;
  149.     serializable = serial;
  150.     externalizable = extern;
  151.  
  152.     /*
  153.      * Enter this class in the table of known descriptors.
  154.      * Otherwise, when the fields are read it may recurse
  155.      * trying to find the descriptor for itself.
  156.      */
  157.     insertDescriptorFor(this);
  158.  
  159.     if (externalizable || name.equals("java.lang.String")) {
  160.         fields = new ObjectStreamField[0];
  161.     } else {
  162.         /* Fill in the list of persistent fields. */
  163.         fields = getFields0(cl);
  164.  
  165.         if (fields.length > 0) {
  166.         /* sort the fields by type and name,
  167.          * primitive fields come first, sorted by name,
  168.          * then object fields, sorted by name.
  169.          */
  170.         boolean done;
  171.         do {
  172.             done = true;
  173.             for (i = fields.length - 1 ; i > 0 ; i--) {
  174.             if (fields[i - 1].compare(fields[i]) > 0) {
  175.                 ObjectStreamField exch = fields[i];
  176.                 fields[i] = fields[i-1];
  177.                 fields[i-1] = exch;
  178.                 done = false;
  179.             }
  180.             }
  181.         } while (!done);
  182.  
  183.         computeFieldSequence();
  184.         }
  185.     }
  186.  
  187.     /* Get the serialVersionUID from the class */
  188.     suid = getSerialVersionUID(cl);
  189.     if (suid == 0) {
  190.         suid = computeSerialVersionUID(cl);
  191.     }
  192.     hasWriteObjectMethod = hasWriteObject(cl);
  193.     }
  194.  
  195.     /*
  196.      * Create an empty ObjectStreamClass for a class about to be read.
  197.      * This is separate from read so ObjectInputStream can assign the
  198.      * wire handle early, before any nested ObjectStreamClasss might
  199.      * be read.
  200.      */
  201.     ObjectStreamClass(String n, long s) {
  202.     name = n;
  203.     suid = s;
  204.     superclass = null;
  205.     }
  206.  
  207.     /*
  208.      * Set the class this version descriptor matches.
  209.      * The name and serializable hash  must match.
  210.      * Compute and fill in the fieldSequence that will be used
  211.      * for reading.
  212.      */
  213.     void setClass(Class cl) throws InvalidClassException {
  214.  
  215.     localClassDesc = lookup(cl);
  216.     
  217.     if (!cl.getName().equals(name) ||
  218.         suid != localClassDesc.suid) {
  219.         throw new InvalidClassException(cl.getName(), "Local class not compatible");
  220.     }
  221.     /*
  222.      * Test that both implement either serializable or externalizable.
  223.      */
  224.     if (serializable != localClassDesc.serializable ||
  225.         externalizable != localClassDesc.externalizable)
  226.         throw new InvalidClassException(cl.getName(),
  227.                     "Serialization incompatible with Externalization");
  228.  
  229.     /* Compute the offsets in the class where each field in this descriptor
  230.      * should be stored.  The fieldSequence is computed from the offsets
  231.      * and used by the native code to read and store the values.
  232.      * Each field in this ObjectStreamClass (the source) is located (by name) in
  233.      * the ObjectStreamClass of the class(the destination).
  234.      * In the usual (non-versioned case) the field is in both
  235.      * descriptors and the types match, so the offset is copied.
  236.      * If the type does not match, a InvalidClass exception is thrown.
  237.      * If the field is not present in the class, the offset is set to -1
  238.      * so the field will be read but discarded.
  239.      * If extra fields are present in the class they are ignored. Their
  240.      * values will be set to the default value by the object allocator.
  241.      * Both the src and dest field list are sorted by type and name.
  242.      */
  243.  
  244.     ObjectStreamField[] destfield = localClassDesc.getFields();
  245.     ObjectStreamField[] srcfield = fields;
  246.  
  247.     int j = 0;
  248.     nextsrc:
  249.     for (int i = 0; i < srcfield.length; i++ ) {
  250.         /* Find this field in the dest*/
  251.         for (int k = j; k < destfield.length; k++) {
  252.           if (srcfield[i].name.equals(destfield[k].name)) {
  253.           /* found match */
  254.           if (!srcfield[i].typeEquals(destfield[k])) {
  255.               throw new InvalidClassException(cl.getName(),
  256.                           "The type of field " +
  257.                                srcfield[i].name +
  258.                                " of class " + name +
  259.                                " is incompatible.");
  260.           }
  261.  
  262.           /* Skip over any fields in the dest that are not in the src */
  263.            j = k; 
  264.           
  265.           srcfield[i].offset = destfield[j].offset;
  266.           // go on to the next source field
  267.           continue nextsrc;
  268.           }
  269.         }
  270.         /* Source field not found in dest, mark field to discard. */
  271.         srcfield[i].offset = -1;
  272.     }
  273.  
  274.     /* Setup the field sequence for native code */
  275.     computeFieldSequence();
  276.  
  277.     /* Remember the class this represents */
  278.     ofClass = cl;
  279.     }
  280.  
  281.     /*
  282.      * Compare the types of two class descriptors.
  283.      * The match if they have the same name and suid
  284.      */
  285.     boolean typeEquals(ObjectStreamClass other) {
  286.     return (suid == other.suid) && name.equals(other.name);
  287.     }
  288.     
  289.     /*
  290.      * Return the array of persistent fields for this class.
  291.      */
  292.     ObjectStreamField[] getFields(){
  293.     return fields;
  294.     }
  295.     
  296.     /*
  297.      * Return the superclass descriptor of this descriptor.
  298.      */
  299.     void setSuperclass(ObjectStreamClass s) {
  300.     superclass = s;
  301.     }
  302.  
  303.     /*
  304.      * Return the superclass descriptor of this descriptor.
  305.      */
  306.     ObjectStreamClass getSuperclass() {
  307.     return superclass;
  308.     }
  309.     
  310.     /*
  311.      * Return whether the class has a writeObject method
  312.      */
  313.     boolean hasWriteObject() {
  314.     return hasWriteObjectMethod;
  315.     }
  316.     
  317.     /*
  318.      * Return the ObjectStreamClass of the local class this one is based on.
  319.      */
  320.     ObjectStreamClass localClassDescriptor() {
  321.     return localClassDesc;
  322.     }
  323.     
  324.     /*
  325.      * Get the externalizability of the class.
  326.      */
  327.     boolean isExternalizable() {
  328.     return externalizable;
  329.     }
  330.  
  331.     /*
  332.      * Get the sequence of fields for this Class.
  333.      */
  334.     int[] getFieldSequence() {
  335.     return fieldSequence;
  336.     }
  337.  
  338.     /*
  339.      * Create the array used by the native code containing
  340.      * the types and offsets to store value read from the stream.
  341.      * The array is an array of int's with the even numbered elements
  342.      * containing the type (first character) and the odd elements
  343.      * containing the offset into the object where the value should be
  344.      * stored.  An offset of -1 means the value should be discarded.
  345.      */
  346.     private void computeFieldSequence() {
  347.     fieldSequence = new int[fields.length*2];
  348.     for (int i = 0; i < fields.length; i++ ) {
  349.         fieldSequence[i*2] = fields[i].type;
  350.         fieldSequence[i*2+1] = fields[i].offset;
  351.     }
  352.     }
  353.     
  354.     /*
  355.      * Compute a hash for the specified class.  Incrementally add
  356.      * items to the hash accumulating in the digest stream.
  357.      * Fold the hash into a long.  Use the SHA secure hash function.
  358.      */
  359.     private static long computeSerialVersionUID(Class thisclass) {
  360.     ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
  361.  
  362.     long h = 0;
  363.     try {
  364.         MessageDigest md = MessageDigest.getInstance("SHA");
  365.         DigestOutputStream mdo = new DigestOutputStream(devnull, md);
  366.         DataOutputStream data = new DataOutputStream(mdo);
  367.  
  368.         data.writeUTF(thisclass.getName());
  369.         
  370.         int classaccess = getClassAccess(thisclass);
  371.         classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
  372.                 Modifier.INTERFACE | Modifier.ABSTRACT);
  373.         data.writeInt(classaccess);
  374.         
  375.         /* 
  376.          * Get the list of interfaces supported,
  377.          * Accumulate their names their names in Lexical order
  378.          * and add them to the hash
  379.          */
  380.         Class interfaces[] = thisclass.getInterfaces();
  381.         quicksort(interfaces);
  382.         
  383.         for (int i = 0; i < interfaces.length; i++) {
  384.         data.writeUTF(interfaces[i].getName());
  385.         }
  386.  
  387.         /* Sort the field names to get a deterministic order */
  388.         String fields[] = getFieldSignatures(thisclass);
  389.         quicksort(fields);
  390.         
  391.         /* Include in the hash all fields except those that are
  392.          * private transient and private static.
  393.          */
  394.         for (int i = 0; i < fields.length; i++) {
  395.         String field = fields[i];
  396.         int access = getFieldAccess(thisclass, field);
  397.         if ((access & M_PRIVATE) == M_PRIVATE &&
  398.             (((access & M_TRANSIENT) == M_TRANSIENT)||
  399.              ((access & M_STATIC) == M_STATIC)))
  400.             continue;
  401.         int offset = field.indexOf(' ');
  402.         String name = field.substring(0, offset);
  403.         String desc = field.substring(offset+1);
  404.         data.writeUTF(name);
  405.         data.writeInt(access);
  406.         data.writeUTF(desc);
  407.         }
  408.  
  409.         /*
  410.          * Get the list of methods including name and signature
  411.          * Sort lexically, add all except the private methods
  412.          * to the hash with their access flags
  413.          */
  414.         String methods[] = getMethodSignatures(thisclass);
  415.         quicksort(methods);
  416.         
  417.         for (int i = 0; i < methods.length; i++) {
  418.         String method = methods[i];
  419.         int access = getMethodAccess(thisclass, method);
  420.         if ((access & M_PRIVATE) != 0)
  421.             continue;
  422.         int offset = method.indexOf(' ');
  423.         String mname = method.substring(0, offset);
  424.         String desc = method.substring(offset+1);
  425.         desc = desc.replace('/', '.');
  426.         data.writeUTF(mname);
  427.         data.writeInt(access);
  428.         data.writeUTF(desc);
  429.         }
  430.  
  431.         /* Compute the hash value for this class.
  432.          * Use only the first 64 bits of the hash.
  433.          */
  434.         byte hasharray[] = md.digest();
  435.         for (int i = 0; i < Math.min(8, hasharray.length); i++) {
  436.         h += (long)(hasharray[i] & 255) << (i * 8);
  437.         }
  438.     } catch (IOException ignore) {
  439.         /* can't happen, but be deterministic anyway. */
  440.         h = -1;
  441.     } catch (NoSuchAlgorithmException complain) {
  442.         throw new SecurityException(complain.getMessage());
  443.     }
  444.     return h;
  445.     }
  446.  
  447.     /* These are in this class so that there is no chance they can be used
  448.      * outside the class.
  449.      */
  450.     private static native int getClassAccess(Class aclass);
  451.  
  452.     private static native String[] getMethodSignatures(Class aclass);
  453.     private static native int getMethodAccess(Class aclass, String methodsig);
  454.  
  455.     private static native String[] getFieldSignatures(Class aclass);
  456.     private static native int getFieldAccess(Class aclass, String fieldsig);
  457.  
  458.     private static final int M_TRANSIENT = 0x0080;
  459.     private static final int M_PRIVATE = 0x0002;
  460.     private static final int M_STATIC = 0x0008;
  461.  
  462.     /*
  463.      * locate the ObjectStreamClass for this class and write it to the stream.
  464.      */
  465.     void write(ObjectOutputStream s) throws IOException {
  466.     
  467.     /* write the flag indicating that this class has write/read object methods */
  468.     int flags = 0;
  469.     if (hasWriteObjectMethod)
  470.         flags |= ObjectStreamConstants.SC_WRITE_METHOD;
  471.     if (serializable)
  472.         flags |= ObjectStreamConstants.SC_SERIALIZABLE;
  473.     if (externalizable)
  474.         flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
  475.     s.writeByte(flags);
  476.     
  477.     /* write the total number of fields */
  478.     s.writeShort(fields.length);
  479.     
  480.     /* Write out the descriptors of the primitive fields Each
  481.      * descriptor consists of the UTF fieldname, a short for the
  482.      * access modes, and the first byte of the signature byte.
  483.      * For the object types, ('[' and 'L'), a reference to the
  484.      * type of the field follows.
  485.      */
  486.     for (int i = 0; i < fields.length; i++ ) {
  487.         ObjectStreamField f = fields[i];
  488.         s.writeByte(f.type);
  489.         s.writeUTF(f.name);
  490.         if (!f.isPrimitive()) {
  491.         s.writeObject(f.typeString);
  492.         }
  493.     }
  494.     }
  495.  
  496.     /*
  497.      * Read the version descriptor from the stream.
  498.      * Write the count of field descriptors
  499.      * for each descriptor write the first character of its type,
  500.      * the name of the field.
  501.      * If the type is for an object either array or object, write
  502.      * the type typedescriptor for the type
  503.      */
  504.     void read(ObjectInputStream s) throws IOException, ClassNotFoundException {
  505.     
  506.     /* read flags and determine whether the source class had
  507.          * write/read methods.
  508.      */
  509.     byte flags = s.readByte();
  510.     hasWriteObjectMethod = (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
  511.     serializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
  512.     externalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
  513.  
  514.     /* Read the number of fields described.
  515.      * For each field read the type byte, the name.
  516.      */    
  517.     int count = s.readShort();
  518.     fields = new ObjectStreamField[count];
  519.     for (int i = 0; i < count; i++ ) {
  520.         char type = (char)s.readByte();
  521.         String name = s.readUTF();
  522.         String ftype = null;
  523.         if (type == '[' || type == 'L') {
  524.         ftype = (String)s.readObject();
  525.         }
  526.         fields[i] = new ObjectStreamField(name, type, -1, ftype);
  527.     }
  528.     }
  529.  
  530.  
  531.     /*
  532.      * Cache of Class -> ClassDescriptor Mappings.
  533.      */
  534.     static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
  535.  
  536.     /*
  537.      * findDescriptorFor a Class.
  538.      * This looks in the cache for a mapping from Class -> ObjectStreamClass mappings.
  539.      * The hashCode of the Class is used for the lookup since the Class is the key.
  540.      * The entries are extended from sun.misc.Ref so the gc will be able to free them
  541.      * if needed.
  542.      */
  543.     private static ObjectStreamClass findDescriptorFor(Class cl) {
  544.  
  545.     int hash = cl.hashCode();
  546.     int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
  547.     ObjectStreamClassEntry e;
  548.     ObjectStreamClassEntry prev;
  549.     
  550.     /* Free any initial entries whose refs have been cleared */
  551.     while ((e = descriptorFor[index]) != null && e.check() == null) {
  552.         descriptorFor[index] = e.next;
  553.     }
  554.  
  555.     /* Traverse the chain looking for a descriptor with ofClass == cl.
  556.      * unlink entries that are unresolved.
  557.      */
  558.     prev = e;
  559.     while (e != null ) {
  560.         ObjectStreamClass desc = (ObjectStreamClass)(e.check());
  561.         if (desc == null) {
  562.         // This entry has been cleared,  unlink it
  563.         prev.next = e.next;
  564.         } else {
  565.         if (desc.ofClass == cl)
  566.             return desc;
  567.         prev = e;
  568.         }
  569.         e = e.next;
  570.     }
  571.     return null;
  572.     }
  573.  
  574.     /*
  575.      * insertDescriptorFor a Class -> ObjectStreamClass mapping.
  576.      */
  577.     private static void insertDescriptorFor(ObjectStreamClass desc) {
  578.     // Make sure not already present
  579.     if (findDescriptorFor(desc.ofClass) != null) {
  580.         return;
  581.     }
  582.  
  583.     int hash = desc.ofClass.hashCode();
  584.     int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
  585.     ObjectStreamClassEntry e = new ObjectStreamClassEntry();
  586.     e.setThing(desc);
  587.     e.next = descriptorFor[index];
  588.            descriptorFor[index] = e;
  589.     }
  590.  
  591.     /*
  592.      * The name of this descriptor
  593.      */
  594.     private String name;
  595.     
  596.     /*
  597.      * The descriptor of the supertype.
  598.      */
  599.     private ObjectStreamClass superclass;
  600.  
  601.     /*
  602.      * Flags for Serializable and Externalizable.
  603.      */
  604.     private boolean serializable;
  605.     private boolean externalizable;
  606.     
  607.     /*
  608.      * Array of persistent fields of this class, sorted by
  609.      * type and name.
  610.      */
  611.     private ObjectStreamField[] fields;
  612.     
  613.     /*
  614.      * Class that is a descriptor for in this virtual machine.
  615.      */
  616.     private Class ofClass;
  617.     
  618.     /* 
  619.      * SerialVersionUID for this class.
  620.      */
  621.     private long suid;
  622.     
  623.     /*
  624.      * This sequence of type, byte offset of the fields to be
  625.      * serialized and deserialized.
  626.      */
  627.     private int[] fieldSequence;
  628.  
  629.     /* True if this class has/had a writeObject method */
  630.     private boolean hasWriteObjectMethod;
  631.     
  632.     /*
  633.      * ObjectStreamClass that this one was built from.
  634.      */
  635.     private ObjectStreamClass localClassDesc;
  636.     
  637.     /* Get the array of non-static and non-transient fields */
  638.     private native ObjectStreamField[] getFields0(Class cl);
  639.     
  640.     /* Get the serialVersionUID from the specified class */
  641.     private static native long getSerialVersionUID(Class cl);
  642.     
  643.     /* Get the boolean as to whether the class has/had a writeObject method. */
  644.     private static native boolean hasWriteObject(Class cl);
  645.     
  646.     /* The Class Object for java.io.Serializable */
  647.     private static Class classSerializable = null;
  648.     private static Class classExternalizable = null;
  649.  
  650.     /*
  651.      * Resolve java.io.Serializable at load time.
  652.      */
  653.     static {
  654.     try {
  655.         classSerializable = Class.forName("java.io.Serializable");
  656.         classExternalizable = Class.forName("java.io.Externalizable");
  657.     } catch (Throwable e) {
  658.         System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
  659.     }
  660.     }
  661.  
  662.     /* Support for quicksort */
  663.  
  664.     /*
  665.      * Implement the doCompare method.
  666.      * Strings are compared directly.
  667.      * Classes are compared using their names.
  668.      * ObjectStreamField objects are compared by type and name
  669.      * and then their descriptors (as strings).
  670.      */
  671.     private static int doCompare(Object o1, Object o2) {
  672.     String s1, s2;
  673.     if (o1 instanceof String && o2 instanceof String) {
  674.         s1 = (String)o1;
  675.         s2 = (String)o2;
  676.     } else if (o1 instanceof Class && o2 instanceof Class) {
  677.         Class c1 = (Class)o1;
  678.         Class c2 = (Class)o2;
  679.         s1 = c1.getName();
  680.         s2 = c2.getName();
  681.     } else if (o1 instanceof ObjectStreamField &&
  682.            o2 instanceof ObjectStreamField) {
  683.         ObjectStreamField f1 = (ObjectStreamField)o1;
  684.         ObjectStreamField f2 = (ObjectStreamField)o2;
  685.         s1 = f1.name;
  686.         s2 = f2.name;
  687.     } else {
  688.         throw new Error("Unsupported types");
  689.     }
  690.     return s1.compareTo(s2);
  691.     }
  692.  
  693.     private static void swap(Object arr[], int i, int j) {
  694.     Object tmp;
  695.  
  696.     tmp = arr[i];
  697.     arr[i] = arr[j];
  698.     arr[j] = tmp;
  699.     }
  700.  
  701.     /*
  702.      * quicksort the array of objects.
  703.      *
  704.      * @param arr[] - an array of objects
  705.      * @param left - the start index - from where to begin sorting
  706.      * @param right - the last index.
  707.      */
  708.     private static void quicksort(Object arr[], int left, int right)
  709.     {
  710.     int i, last;
  711.  
  712.     if (left >= right) { /* do nothing if array contains fewer than two */
  713.         return;          /* two elements */
  714.     }
  715.     swap(arr, left, (left+right) / 2);
  716.     last = left;
  717.     for (i = left+1; i <= right; i++) {
  718.         if (doCompare(arr[i], arr[left]) < 0) {
  719.         swap(arr, ++last, i);
  720.         }
  721.     }
  722.     swap(arr, left, last);
  723.     quicksort(arr, left, last-1);
  724.     quicksort(arr, last+1, right);
  725.     }
  726.  
  727.     /*
  728.      * Preform a sort using the specified comparitor object.
  729.      */       
  730.     private static void quicksort(Object arr[]) {
  731.         quicksort(arr, 0, arr.length-1);
  732.     }
  733. }
  734.  
  735.  
  736. /*
  737.  * Entries held in the Cache of known ObjectStreamClass objects.
  738.  * Entries are chained together with the same hash value (modulo array size).
  739.  */
  740. class ObjectStreamClassEntry extends sun.misc.Ref {
  741.     ObjectStreamClassEntry next;
  742.     public Object reconstitute() {
  743.     return null;
  744.     }
  745. }
  746.