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

  1. /*
  2.  * @(#)ObjectStreamClass.java    1.65 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.security.MessageDigest;
  18. import java.security.NoSuchAlgorithmException;
  19. import java.security.DigestOutputStream;
  20. import java.security.AccessController;
  21.  
  22. import java.lang.reflect.AccessibleObject;
  23. import java.lang.reflect.Modifier;
  24. import java.lang.reflect.Field;
  25. import java.lang.reflect.Member;
  26. import java.lang.reflect.Method;
  27. import java.lang.reflect.Constructor;
  28.  
  29. import java.util.Arrays;
  30. import java.util.Comparator;
  31. import java.util.Iterator;
  32.  
  33. /**
  34.  * A ObjectStreamClass describes a class that can be serialized to a stream
  35.  * or a class that was serialized to a stream.  It contains the name
  36.  * and the serialVersionUID of the class.
  37.  * <br>
  38.  * The ObjectStreamClass for a specific class loaded in this Java VM can
  39.  * be found using the lookup method.
  40.  *
  41.  * @author  Roger Riggs
  42.  * @version @(#)ObjectStreamClass.java    1.45 97/08/03
  43.  * @since   JDK1.1
  44.  */
  45. public class ObjectStreamClass implements java.io.Serializable {
  46.  
  47.     /** Find the descriptor for a class that can be serialized.  Null
  48.      * is returned if the specified class does not implement
  49.      * java.io.Serializable or java.io.Externalizable.
  50.      */
  51.     public static ObjectStreamClass lookup(Class cl)
  52.     {
  53.     ObjectStreamClass desc = lookupInternal(cl);
  54.     if (desc.isSerializable() || desc.isExternalizable())
  55.         return desc;
  56.     return null;
  57.     }
  58.     /*
  59.      * Find the class descriptor for the specified class.
  60.      * Package access only so it can be called from ObjectIn/OutStream.
  61.      */
  62.     static ObjectStreamClass lookupInternal(Class cl)
  63.     {
  64.     /* Synchronize on the hashtable so no two threads will do
  65.      * this at the same time.
  66.      */
  67.     ObjectStreamClass desc = null;
  68.     synchronized (descriptorFor) {
  69.         /* Find the matching descriptor if it already known */
  70.         desc = findDescriptorFor(cl);
  71.         if (desc != null) {
  72.         return desc;
  73.         }
  74.         
  75.         /* Check if it's serializable or externalizable.
  76.          * Test if it's Externalizable, clear the serializable flag.
  77.          * Only one or the other may be set in the protocol.
  78.          */
  79.         boolean serializable = classSerializable.isAssignableFrom(cl);
  80.         boolean externalizable = classExternalizable.isAssignableFrom(cl);
  81.         if (externalizable)
  82.         serializable = false;
  83.  
  84.         /* If the class is only Serializable,
  85.          * lookup the descriptor for the superclass.
  86.          */
  87.         ObjectStreamClass superdesc = null;
  88.         if (serializable) {
  89.         Class superclass = cl.getSuperclass();
  90.         if (superclass != null) 
  91.             superdesc = lookup(superclass);
  92.         }
  93.  
  94.         /* Create a new version descriptor,
  95.          * it put itself in the known table.
  96.          */
  97.         desc = new ObjectStreamClass(cl, superdesc,
  98.                       serializable, externalizable);
  99.     }
  100.     return desc;
  101.     }
  102.     
  103.     /**
  104.      * The name of the class described by this descriptor.
  105.      */
  106.     public String getName() {
  107.     return name;
  108.     }
  109.  
  110.     /**
  111.      * Return the serialVersionUID for this class.
  112.      * The serialVersionUID defines a set of classes all with the same name
  113.      * that have evolved from a common root class and agree to be serialized
  114.      * and deserialized using a common format.
  115.      */
  116.     public long getSerialVersionUID() {
  117.     return suid;
  118.     }
  119.  
  120.     /**
  121.      * Return the class in the local VM that this version is mapped to.
  122.      * Null is returned if there is no corresponding local class.
  123.      */
  124.     public Class forClass() {
  125.     return ofClass;
  126.     }
  127.  
  128.     /**
  129.      * Return an array of the fields of this serializable class.
  130.      * @return an array containing an element for each persistent
  131.      * field of this class. Returns an array of length zero if
  132.      * there are no fields.
  133.      * @since JDK1.2
  134.      */
  135.     public ObjectStreamField[] getFields() {
  136.         // Return a copy so the caller can't change the fields.
  137.     if (fields.length > 0) {
  138.         ObjectStreamField[] dup = new ObjectStreamField[fields.length];
  139.         System.arraycopy(fields, 0, dup, 0, fields.length);
  140.         return dup;
  141.     } else {
  142.         return fields;
  143.     }
  144.     }
  145.  
  146.     /* Avoid unnecessary allocations. */
  147.     final ObjectStreamField[] getFieldsNoCopy() {
  148.     return fields;
  149.     }
  150.  
  151.     /**
  152.      * Get the field of this class by name.
  153.      * @return The ObjectStreamField object of the named field or null if there
  154.      * is no such named field.
  155.      */
  156.     public ObjectStreamField getField(String name) {
  157.         /* Binary search of fields by name.
  158.         */
  159.         for (int i = fields.length-1; i >= 0; i--) {
  160.             if (name.equals(fields[i].getName())) {
  161.                 return fields[i];
  162.         }
  163.         }
  164.     return null;
  165.     }
  166.  
  167.     /**
  168.      * Return a string describing this ObjectStreamClass.
  169.      */
  170.     public String toString() {
  171.     StringBuffer sb = new StringBuffer();
  172.  
  173.     sb.append(name);
  174.     sb.append(": static final long serialVersionUID = ");
  175.     sb.append(Long.toString(suid));
  176.     sb.append("L;");
  177.     return sb.toString();
  178.     }
  179.  
  180.     /*
  181.      * Create a new ObjectStreamClass from a loaded class.
  182.      * Don't call this directly, call lookup instead.
  183.      */
  184.     private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc,
  185.                   boolean serial, boolean extern)
  186.     {
  187.     ofClass = cl;        /* created from this class */
  188.  
  189.     name = cl.getName();
  190.     superclass = superdesc;
  191.     serializable = serial;
  192.     externalizable = extern;
  193.  
  194.     /*
  195.      * Enter this class in the table of known descriptors.
  196.      * Otherwise, when the fields are read it may recurse
  197.      * trying to find the descriptor for itself.
  198.      */
  199.     insertDescriptorFor(this);
  200.  
  201.     if (!serializable ||
  202.         externalizable ||
  203.         name.equals("java.lang.String")) {
  204.         fields = new ObjectStreamField[0];
  205.     } else if (serializable) {
  206.         try {
  207.         /* Ask for permission to override field access checks.
  208.          */
  209.         AccessController.beginPrivileged();
  210.         
  211.         /* Fill in the list of persistent fields.
  212.          * If it is declared, use the declared serialPersistentFields.
  213.          * Otherwise, extract the fields from the class itself.
  214.          */
  215.         try {
  216.             Field pf = cl.getDeclaredField("serialPersistentFields");
  217.             pf.setAccessible(true);
  218.             ObjectStreamField[] f = (ObjectStreamField[])pf.get(cl);
  219.             fields = f;
  220.         } catch (NoSuchFieldException e) {
  221.             fields = null;
  222.         } catch (IllegalAccessException e) {
  223.             fields = null;
  224.         } catch (IllegalArgumentException e) {
  225.             fields = null;
  226.         }
  227.  
  228.         if (fields == null) {
  229.             /* Get all of the declared fields for this
  230.              * Class. setAccessible on all fields so they
  231.              * can be accessed later.  Create a temporary
  232.              * ObjectStreamField array to hold each
  233.              * non-static, non-transient field. Then copy the
  234.              * temporary array into an array of the correct
  235.              * size once the number of fields is known.
  236.              */
  237.             Field[] actualfields = cl.getDeclaredFields();
  238.             AccessibleObject.setAccessible(actualfields, true);
  239.  
  240.             int numFields = 0;
  241.             ObjectStreamField[] tempFields = 
  242.             new ObjectStreamField[actualfields.length];
  243.             for (int i = 0; i < actualfields.length; i++) {
  244.             int modifiers = actualfields[i].getModifiers();
  245.             if (!Modifier.isStatic(modifiers) &&
  246.                 !Modifier.isTransient(modifiers)) {
  247.                 tempFields[numFields++] =
  248.                 new ObjectStreamField(actualfields[i]);
  249.             }
  250.             }
  251.             fields = new ObjectStreamField[numFields];
  252.             System.arraycopy(tempFields, 0, fields, 0, numFields);
  253.  
  254.         } else {
  255.             // For each declared persistent field, look for an actual
  256.             // reflected Field. If there is one, make sure it's the correct
  257.             // type and cache it in the ObjectStreamClass for that field.
  258.             for (int j = fields.length-1; j >= 0; j--) {
  259.             try {
  260.                 Field reflField = cl.getDeclaredField(fields[j].getName());
  261.                 if (fields[j].getType() == reflField.getType()) {
  262.                 reflField.setAccessible(true);
  263.                 fields[j].setField(reflField);
  264.                 } else {
  265.                 // TBD: Should this be flagged as an error?
  266.                 }
  267.             } catch (NoSuchFieldException e) {
  268.                 // Nothing to do
  269.             }
  270.             }
  271.         }
  272.         } finally {
  273.         AccessController.endPrivileged();
  274.         }
  275.  
  276.         if (fields.length > 1)
  277.         Arrays.sort(fields);
  278.  
  279.         /* Set up field data for use while writing using the API api. */
  280.         computeFieldInfo();
  281.     }
  282.  
  283.     /* Get the serialVersionUID from the class.
  284.      * It uses the access override mechanism so make sure
  285.      * the field objects is only used here.
  286.      */
  287.     try {
  288.         AccessController.beginPrivileged();
  289.         Field f = cl.getDeclaredField("serialVersionUID");
  290.         int mods = f.getModifiers();
  291.         if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
  292.         f.setAccessible(true);
  293.         suid = f.getLong(cl);
  294.         } else {
  295.         suid = computeSerialVersionUID(cl);
  296.         }
  297.     } catch (NoSuchFieldException ex) {
  298.         suid = computeSerialVersionUID(cl);
  299.     } catch (IllegalAccessException ex) {
  300.         suid = computeSerialVersionUID(cl);
  301.     } finally {
  302.         AccessController.endPrivileged();
  303.     }
  304.  
  305.     /* Look for the writeObject method
  306.      * Set the accessible flag on it here. ObjectOutputStream
  307.      * will call it as necessary.
  308.      */
  309.     try {
  310.         Class[] args = {ObjectOutputStream.class};
  311.         AccessController.beginPrivileged();
  312.         writeObjectMethod = cl.getDeclaredMethod("writeObject", args);
  313.         hasWriteObjectMethod = true;
  314.         int mods = writeObjectMethod.getModifiers();
  315.         // Method must be private and non-static
  316.         if (!Modifier.isPrivate(mods) ||
  317.         Modifier.isStatic(mods)) {
  318.         writeObjectMethod = null;
  319.         hasWriteObjectMethod = false;
  320.         } else {
  321.         writeObjectMethod.setAccessible(true);
  322.         }
  323.     } catch (NoSuchMethodException e) {
  324.     } finally {
  325.         AccessController.endPrivileged();
  326.     }
  327.     }
  328.  
  329.     /*
  330.      * Create an empty ObjectStreamClass for a class about to be read.
  331.      * This is separate from read so ObjectInputStream can assign the
  332.      * wire handle early, before any nested ObjectStreamClass might
  333.      * be read.
  334.      */
  335.     ObjectStreamClass(String n, long s) {
  336.     name = n;
  337.     suid = s;
  338.     superclass = null;
  339.     }
  340.  
  341.     /*
  342.      * Set the class this version descriptor matches.
  343.      * The base class name and serializable hash must match.
  344.      * Fill in the reflected Fields that will be used
  345.      * for reading.
  346.      */
  347.     void setClass(Class cl) throws InvalidClassException {
  348.  
  349.     if (cl == null) {
  350.         localClassDesc = null;
  351.         ofClass = null;
  352.         computeFieldInfo();
  353.         return;
  354.     }
  355.  
  356.     localClassDesc = lookupInternal(cl);
  357.     if (localClassDesc == null)
  358.         throw new InvalidClassException(cl.getName(), 
  359.                         "Local class not compatible");
  360.     if (suid != localClassDesc.suid) {
  361.         
  362.         /* Disregard the serialVersionUID of an array
  363.          * when name and cl.Name differ. If resolveClass() returns
  364.          * an array with a different package name,
  365.          * the serialVersionUIDs will not match since the fully
  366.          * qualified array class is used in the
  367.          * computation of the array's serialVersionUID. There is
  368.          * no way to set a permanent serialVersionUID for an array type.
  369.          */
  370.         if (! (cl.isArray() && ! cl.getName().equals(name)))
  371.         throw new InvalidClassException(cl.getName(), 
  372.             "Local class not compatible:" + 
  373.             " stream classdesc serialVersionUID=" + suid +
  374.             " local class serialVersionUID=" + localClassDesc.suid);
  375.     }
  376.  
  377.     /* compare the class names, stripping off package names. */
  378.     if (! compareClassNames(name, cl.getName(), '.'))
  379.         throw new InvalidClassException(name,
  380.                         "Incompatible local class name: " +
  381.                         cl.getName());
  382.  
  383.     /*
  384.      * Test that both implement either serializable or externalizable.
  385.      */
  386.     if (serializable != localClassDesc.serializable ||
  387.         externalizable != localClassDesc.externalizable)
  388.         throw new InvalidClassException(cl.getName(),
  389.                     "Serialization incompatible with Externalization");
  390.  
  391.     /* Set up the reflected Fields in the class where the value of each 
  392.      * field in this descriptor should be stored.
  393.      * Each field in this ObjectStreamClass (the source) is located (by 
  394.      * name) in the ObjectStreamClass of the class(the destination).
  395.      * In the usual (non-versioned case) the field is in both
  396.      * descriptors and the types match, so the reflected Field is copied.
  397.      * If the type does not match, a InvalidClass exception is thrown.
  398.      * If the field is not present in the class, the reflected Field 
  399.      * remains null so the field will be read but discarded.
  400.      * If extra fields are present in the class they are ignored. Their
  401.      * values will be set to the default value by the object allocator.
  402.      * Both the src and dest field list are sorted by type and name.
  403.      */
  404.  
  405.     ObjectStreamField[] destfield = 
  406.         (ObjectStreamField[])localClassDesc.fields;
  407.     ObjectStreamField[] srcfield = 
  408.         (ObjectStreamField[])fields;
  409.  
  410.     int j = 0;
  411.     nextsrc:
  412.     for (int i = 0; i < srcfield.length; i++ ) {
  413.         /* Find this field in the dest*/
  414.         for (int k = j; k < destfield.length; k++) {
  415.           if (srcfield[i].getName().equals(destfield[k].getName())) {
  416.           /* found match */
  417.           if (srcfield[i].isPrimitive() && 
  418.               !srcfield[i].typeEquals(destfield[k])) {
  419.               throw new InvalidClassException(cl.getName(),
  420.                           "The type of field " +
  421.                                srcfield[i].getName() +
  422.                                " of class " + name +
  423.                                " is incompatible.");
  424.           }
  425.  
  426.           /* Skip over any fields in the dest that are not in the src */
  427.            j = k; 
  428.           
  429.           srcfield[i].setField(destfield[j].getField());
  430.           // go on to the next source field
  431.           continue nextsrc;
  432.           }
  433.         }
  434.     }
  435.  
  436.     /* Set up field data for use while reading from the input stream. */
  437.     computeFieldInfo();
  438.  
  439.     /* Remember the class this represents */
  440.     ofClass = cl;
  441.  
  442.     /* Look for the readObject method
  443.      * set the access override and save the reference for
  444.      * ObjectInputStream so it can all the method directly.
  445.      */
  446.     try {
  447.         Class[] args = {java.io.ObjectInputStream.class};
  448.         AccessController.beginPrivileged();
  449.         readObjectMethod = cl.getDeclaredMethod("readObject", args);
  450.         int mods = readObjectMethod.getModifiers();
  451.         // Method must be private and non-static
  452.         if (!Modifier.isPrivate(mods) ||
  453.         Modifier.isStatic(mods)) {
  454.         readObjectMethod = null;
  455.         } else {
  456.         readObjectMethod.setAccessible(true);
  457.         }
  458.     } catch (NoSuchMethodException e) {
  459.     } finally {
  460.         AccessController.endPrivileged();
  461.     }
  462.     }
  463.  
  464.     /* Compare the base class names of streamName and localName.
  465.      * 
  466.      * @return  Return true iff the base class name compare.
  467.      * @parameter streamName    Fully qualified class name.
  468.      * @parameter localName    Fully qualified class name.
  469.      * @parameter pkgSeparator    class names use either '.' or '/'.
  470.      * 
  471.      * Only compare base class name to allow package renaming.
  472.      */
  473.     static boolean compareClassNames(String streamName,
  474.                      String localName,
  475.                      char pkgSeparator) {
  476.     /* compare the class names, stripping off package names. */
  477.     int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
  478.     if (streamNameIndex < 0) 
  479.         streamNameIndex = 0;
  480.  
  481.     int localNameIndex = localName.lastIndexOf(pkgSeparator);
  482.     if (localNameIndex < 0)
  483.         localNameIndex = 0;
  484.  
  485.     return streamName.regionMatches(false, streamNameIndex, 
  486.                     localName, localNameIndex,
  487.                     streamName.length() - streamNameIndex);
  488.     }
  489.  
  490.     /*
  491.      * Compare the types of two class descriptors.
  492.      * They match if they have the same class name and suid
  493.      */
  494.     boolean typeEquals(ObjectStreamClass other) {
  495.     return (suid == other.suid) &&
  496.         compareClassNames(name, other.name, '.');
  497.     }
  498.     
  499.     /*
  500.      * Return the superclass descriptor of this descriptor.
  501.      */
  502.     void setSuperclass(ObjectStreamClass s) {
  503.     superclass = s;
  504.     }
  505.  
  506.     /*
  507.      * Return the superclass descriptor of this descriptor.
  508.      */
  509.     ObjectStreamClass getSuperclass() {
  510.     return superclass;
  511.     }
  512.     
  513.     /*
  514.      * Return whether the class has a writeObject method
  515.      */
  516.     boolean hasWriteObject() {
  517.     return hasWriteObjectMethod;
  518.     }
  519.     
  520.     /*
  521.      * Return true if all instances of 'this' Externalizable class 
  522.      * are written in block-data mode from the stream that 'this' was read
  523.      * from. <p>
  524.      *
  525.      * In JDK 1.1, all Externalizable instances are not written 
  526.      * in block-data mode.
  527.      * In JDK 1.2, all Externalizable instances, by default, are written
  528.      * in block-data mode and the Externalizable instance is terminated with
  529.      * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable 
  530.      * instances.
  531.      *
  532.      * IMPLEMENTATION NOTE:
  533.      *   This should have been a mode maintained per stream; however,
  534.      *   for compatibility reasons, it was only possible to record
  535.      *   this change per class. All Externalizable classes within
  536.      *   a given stream should either have this mode enabled or 
  537.      *   disabled. This is enforced by not allowing the PROTOCOL_VERSION
  538.      *   of a stream to he changed after any objects have been written.
  539.      *
  540.      * @see ObjectOuputStream#useProtocolVersion
  541.      * @see ObjectStreamConstants#PROTOCOL_VERSION_1
  542.      * @see ObjectStreamConstants#PROTOCOL_VERSION_2
  543.      *
  544.      * @since JDK 1.2
  545.      */
  546.     boolean hasExternalizableBlockDataMode() {
  547.     return hasExternalizableBlockData;
  548.     }
  549.  
  550.     /*
  551.      * Return the ObjectStreamClass of the local class this one is based on.
  552.      */
  553.     ObjectStreamClass localClassDescriptor() {
  554.     return localClassDesc;
  555.     }
  556.     
  557.     /*
  558.      * Get the Serializability of the class.
  559.      */
  560.     boolean isSerializable() {
  561.     return serializable;
  562.     }
  563.  
  564.     /*
  565.      * Get the externalizability of the class.
  566.      */
  567.     boolean isExternalizable() {
  568.     return externalizable;
  569.     }
  570.  
  571.     /*
  572.      * Calculate the size of the array needed to store primitive data and the
  573.      * number of object references to read when reading from the input 
  574.      * stream.
  575.      */
  576.     private void computeFieldInfo() {
  577.     primBytes = 0;
  578.     objFields = 0;
  579.  
  580.     for (int i = 0; i < fields.length; i++ ) {
  581.         switch (fields[i].getTypeCode()) {
  582.         case 'B':
  583.         case 'Z':
  584.             fields[i].setOffset(primBytes);
  585.             primBytes += 1;
  586.             break;
  587.         case 'C':
  588.         case 'S': 
  589.         fields[i].setOffset(primBytes);
  590.             primBytes += 2;
  591.             break;
  592.  
  593.         case 'I':
  594.         case 'F': 
  595.             fields[i].setOffset(primBytes);
  596.             primBytes += 4;
  597.             break;
  598.         case 'J':
  599.         case 'D' :
  600.         fields[i].setOffset(primBytes);
  601.             primBytes += 8;
  602.             break;
  603.         
  604.         case 'L':
  605.         case '[':
  606.             fields[i].setOffset(objFields);
  607.             objFields += 1;
  608.             break;
  609.         }
  610.     }
  611.     }
  612.     
  613.     /*
  614.      * Compute a hash for the specified class.  Incrementally add
  615.      * items to the hash accumulating in the digest stream.
  616.      * Fold the hash into a long.  Use the SHA secure hash function.
  617.      */
  618.     private static long computeSerialVersionUID(Class cl) {
  619.     ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
  620.  
  621.     long h = 0;
  622.     try {
  623.         MessageDigest md = MessageDigest.getInstance("SHA");
  624.         DigestOutputStream mdo = new DigestOutputStream(devnull, md);
  625.         DataOutputStream data = new DataOutputStream(mdo);
  626.  
  627.  
  628.         data.writeUTF(cl.getName());
  629.         
  630.         int classaccess = cl.getModifiers();
  631.         classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
  632.                 Modifier.INTERFACE | Modifier.ABSTRACT);
  633.  
  634.         /* Workaround for javac bug that only set ABSTRACT for
  635.          * interfaces if the interface had some methods.
  636.          * The ABSTRACT bit reflects that the number of methods > 0.
  637.          * This is required so correct hashes can be computed
  638.          * for existing class files.
  639.          * Previously this hack was previously present in the VM.
  640.          */
  641.         Method[] method = cl.getDeclaredMethods();
  642.         if ((classaccess & Modifier.INTERFACE) != 0) {
  643.         classaccess &= (~Modifier.ABSTRACT);
  644.         if (method.length > 0) {
  645.             classaccess |= Modifier.ABSTRACT;
  646.         }
  647.         }
  648.  
  649.         data.writeInt(classaccess);
  650.  
  651.         /* 
  652.          * Get the list of interfaces supported,
  653.          * Accumulate their names their names in Lexical order
  654.          * and add them to the hash
  655.          */
  656.         Class interfaces[] = cl.getInterfaces();
  657.         Arrays.sort(interfaces, compareClassByName);
  658.         
  659.         for (int i = 0; i < interfaces.length; i++) {
  660.         data.writeUTF(interfaces[i].getName());
  661.         }
  662.  
  663.         /* Sort the field names to get a deterministic order */
  664.         Field[] field = cl.getDeclaredFields();
  665.         Arrays.sort(field, compareMemberByName);
  666.  
  667.         for (int i = 0; i < field.length; i++) {
  668.         Field f = field[i];
  669.  
  670.         /* Include in the hash all fields except those that are
  671.          * private transient and private static.
  672.          */
  673.         int m = f.getModifiers();
  674.         if (Modifier.isPrivate(m) && 
  675.             (Modifier.isTransient(m) || Modifier.isStatic(m)))
  676.             continue;
  677.  
  678.         data.writeUTF(f.getName());
  679.         data.writeInt(m);
  680.         data.writeUTF(getSignature(f.getType()));
  681.         }
  682.  
  683.         if (hasStaticInitializer(cl)) {
  684.         data.writeUTF("<clinit>");
  685.         data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have
  686.         data.writeUTF("()V");
  687.         }
  688.  
  689.         /*
  690.          * Get the list of constructors including name and signature
  691.          * Sort lexically, add all except the private constructors
  692.          * to the hash with their access flags
  693.          */
  694.  
  695.         MethodSignature[] constructors =
  696.         MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors());
  697.         for (int i = 0; i < constructors.length; i++) {
  698.         MethodSignature c = constructors[i];
  699.         String mname = "<init>";
  700.         String desc = c.signature;
  701.         desc = desc.replace('/', '.');
  702.         data.writeUTF(mname);
  703.         data.writeInt(c.member.getModifiers());
  704.         data.writeUTF(desc);
  705.         }
  706.  
  707.         /* Include in the hash all methods except those that are
  708.          * private transient and private static.
  709.          */
  710.         MethodSignature[] methods =
  711.         MethodSignature.removePrivateAndSort(method);
  712.         for (int i = 0; i < methods.length; i++ ) {
  713.         MethodSignature m = methods[i];    
  714.         String desc = m.signature;
  715.         desc = desc.replace('/', '.');
  716.         data.writeUTF(m.member.getName());
  717.         data.writeInt(m.member.getModifiers());
  718.         data.writeUTF(desc);
  719.         }
  720.  
  721.         /* Compute the hash value for this class.
  722.          * Use only the first 64 bits of the hash.
  723.          */
  724.         data.flush();
  725.         byte hasharray[] = md.digest();
  726.         for (int i = 0; i < Math.min(8, hasharray.length); i++) {
  727.         h += (long)(hasharray[i] & 255) << (i * 8);
  728.         }
  729.     } catch (IOException ignore) {
  730.         /* can't happen, but be deterministic anyway. */
  731.         h = -1;
  732.     } catch (NoSuchAlgorithmException complain) {
  733.         throw new SecurityException(complain.getMessage());
  734.     }
  735.     return h;
  736.     }
  737.  
  738.  
  739.     /**
  740.       * Compute the JVM signature for the class.
  741.       */
  742.     static String getSignature(Class clazz) {
  743.     String type = null;
  744.     if (clazz.isArray()) {
  745.         Class cl = clazz;
  746.         int dimensions = 0;
  747.         while (cl.isArray()) {
  748.         dimensions++;
  749.         cl = cl.getComponentType();
  750.         }
  751.         StringBuffer sb = new StringBuffer();
  752.         for (int i = 0; i < dimensions; i++) {
  753.         sb.append("[");
  754.         }
  755.         sb.append(getSignature(cl));
  756.         type = sb.toString();
  757.     } else if (clazz.isPrimitive()) {
  758.         if (clazz == Integer.TYPE) {
  759.         type = "I";
  760.         } else if (clazz == Byte.TYPE) {
  761.         type = "B";
  762.         } else if (clazz == Long.TYPE) {
  763.         type = "J";
  764.         } else if (clazz == Float.TYPE) {
  765.         type = "F";
  766.         } else if (clazz == Double.TYPE) {
  767.         type = "D";
  768.         } else if (clazz == Short.TYPE) {
  769.         type = "S";
  770.         } else if (clazz == Character.TYPE) {
  771.         type = "C";
  772.         } else if (clazz == Boolean.TYPE) {
  773.         type = "Z";
  774.         } else if (clazz == Void.TYPE) {
  775.         type = "V";
  776.         }
  777.     } else {
  778.         type = "L" + clazz.getName().replace('.', '/') + ";";
  779.     }
  780.     return type;
  781.     }
  782.  
  783.     /*
  784.      * Compute the JVM method descriptor for the method.
  785.      */
  786.     static String getSignature(Method meth) {
  787.     StringBuffer sb = new StringBuffer();
  788.  
  789.     sb.append("(");
  790.  
  791.     Class[] params = meth.getParameterTypes(); // avoid clone
  792.     for (int j = 0; j < params.length; j++) {
  793.         sb.append(getSignature(params[j]));
  794.     }
  795.     sb.append(")");
  796.     sb.append(getSignature(meth.getReturnType()));
  797.     return sb.toString();
  798.     }
  799.  
  800.     /*
  801.      * Compute the JVM constructor descriptor for the constructor.
  802.      */
  803.     static String getSignature(Constructor cons) {
  804.     StringBuffer sb = new StringBuffer();
  805.  
  806.     sb.append("(");
  807.  
  808.     Class[] params = cons.getParameterTypes(); // avoid clone
  809.     for (int j = 0; j < params.length; j++) {
  810.         sb.append(getSignature(params[j]));
  811.     }
  812.     sb.append(")V");
  813.     return sb.toString();
  814.     }
  815.  
  816.  
  817.     /*
  818.      * locate the ObjectStreamClass for this class and write it to the stream.
  819.      */
  820.     void write(ObjectOutputStream s) throws IOException {
  821.     
  822.     /* write the flag indicating that this class has write/read object methods */
  823.     int flags = 0;
  824.     if (hasWriteObjectMethod)
  825.         flags |= ObjectStreamConstants.SC_WRITE_METHOD;
  826.     if (serializable)
  827.         flags |= ObjectStreamConstants.SC_SERIALIZABLE;
  828.     if (externalizable) {
  829.         flags |=  ObjectStreamConstants.SC_EXTERNALIZABLE;
  830.  
  831.         /* Enabling the SC_BLOCK_DATA flag indicates PROTCOL_VERSION_2.*/
  832.         if (! s.useDeprecatedExternalizableFormat)
  833.         flags |= ObjectStreamConstants.SC_BLOCK_DATA;
  834.         }
  835.     s.writeByte(flags);
  836.     
  837.     // If there are no fields, write a null and return
  838.     if (fields == null) {
  839.         s.writeShort(0);
  840.         return;
  841.     }
  842.  
  843.     /* write the total number of fields */
  844.     s.writeShort(fields.length);
  845.     
  846.     /* Write out the descriptors of the primitive fields Each
  847.      * descriptor consists of the UTF fieldname, a short for the
  848.      * access modes, and the first byte of the signature byte.
  849.      * For the object types, ('[' and 'L'), a reference to the
  850.      * type of the field follows.
  851.      */
  852.  
  853.     /* disable replacement of String objects written
  854.      * by ObjectStreamClass. */
  855.     boolean prevEnableReplace = s.enableReplace;
  856.     s.enableReplace = false;
  857.     try {
  858.         for (int i = 0; i < fields.length; i++ ) {
  859.         ObjectStreamField f = fields[i];
  860.         s.writeByte(f.getTypeCode());
  861.         s.writeUTF(f.getName());
  862.         if (!f.isPrimitive()) {
  863.             s.writeObject(f.getTypeString());
  864.         }
  865.         }
  866.     } finally {
  867.         s.enableReplace = prevEnableReplace;
  868.     }
  869.     }
  870.  
  871.     /*
  872.      * Read the version descriptor from the stream.
  873.      * Write the count of field descriptors
  874.      * for each descriptor write the first character of its type,
  875.      * the name of the field.
  876.      * If the type is for an object either array or object, write
  877.      * the type typedescriptor for the type
  878.      */
  879.     void read(ObjectInputStream s) throws IOException, ClassNotFoundException {
  880.     
  881.     /* read flags and determine whether the source class had
  882.          * write/read methods.
  883.      */
  884.     byte flags = s.readByte();
  885.     serializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
  886.     externalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
  887.     hasWriteObjectMethod = serializable ?
  888.         (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0 :
  889.         false;
  890.     hasExternalizableBlockData = externalizable ? 
  891.         (flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0 :
  892.         false;
  893.                      
  894.  
  895.     /* Read the number of fields described.
  896.      * For each field read the type byte, the name.
  897.      */    
  898.     int count = s.readShort();
  899.     fields = new ObjectStreamField[count];
  900.  
  901.     /* disable replacement of String objects read
  902.      * by ObjectStreamClass. */
  903.     boolean prevEnableResolve = s.enableResolve;
  904.     s.enableResolve = false;
  905.     try {
  906.         for (int i = 0; i < count; i++ ) {
  907.         char type = (char)s.readByte();
  908.         String name = s.readUTF();
  909.         String ftype = null;
  910.         if (type == '[' || type == 'L') {
  911.             ftype = (String)s.readObject();
  912.         }
  913.         fields[i] = 
  914.             new ObjectStreamField(name, type, null, ftype);
  915.         }
  916.     } finally {
  917.         s.enableResolve = prevEnableResolve;
  918.     }
  919.     }
  920.  
  921.  
  922.     /*
  923.      * Cache of Class -> ClassDescriptor Mappings.
  924.      */
  925.     static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
  926.  
  927.     /*
  928.      * findDescriptorFor a Class.  This looks in the cache for a
  929.      * mapping from Class -> ObjectStreamClass mappings.  The hashCode
  930.      * of the Class is used for the lookup since the Class is the key.
  931.      * The entries are extended from java.lang.ref.SoftReference so the
  932.      * gc will be able to free them if needed.
  933.      */
  934.     private static ObjectStreamClass findDescriptorFor(Class cl) {
  935.  
  936.     int hash = cl.hashCode();
  937.     int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
  938.     ObjectStreamClassEntry e;
  939.     ObjectStreamClassEntry prev;
  940.     
  941.     /* Free any initial entries whose refs have been cleared */
  942.     while ((e = descriptorFor[index]) != null && e.get() == null) {
  943.         descriptorFor[index] = e.next;
  944.     }
  945.  
  946.     /* Traverse the chain looking for a descriptor with ofClass == cl.
  947.      * unlink entries that are unresolved.
  948.      */
  949.     prev = e;
  950.     while (e != null ) {
  951.         ObjectStreamClass desc = (ObjectStreamClass)(e.get());
  952.         if (desc == null) {
  953.         // This entry has been cleared,  unlink it
  954.         prev.next = e.next;
  955.         } else {
  956.         if (desc.ofClass == cl)
  957.             return desc;
  958.         prev = e;
  959.         }
  960.         e = e.next;
  961.     }
  962.     return null;
  963.     }
  964.  
  965.     /*
  966.      * insertDescriptorFor a Class -> ObjectStreamClass mapping.
  967.      */
  968.     private static void insertDescriptorFor(ObjectStreamClass desc) {
  969.     // Make sure not already present
  970.     if (findDescriptorFor(desc.ofClass) != null) {
  971.         return;
  972.     }
  973.  
  974.     int hash = desc.ofClass.hashCode();
  975.     int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
  976.     ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
  977.     e.next = descriptorFor[index];
  978.            descriptorFor[index] = e;
  979.     }
  980.  
  981.     /*
  982.      * The name of this descriptor
  983.      */
  984.     private String name;
  985.     
  986.     /*
  987.      * The descriptor of the supertype.
  988.      */
  989.     private ObjectStreamClass superclass;
  990.  
  991.     /*
  992.      * Flags for Serializable and Externalizable.
  993.      */
  994.     private boolean serializable;
  995.     private boolean externalizable;
  996.     
  997.     /*
  998.      * Array of persistent fields of this class, sorted by
  999.      * type and name.
  1000.      */
  1001.     private ObjectStreamField[] fields;
  1002.     
  1003.     /*
  1004.      * Class that is a descriptor for in this virtual machine.
  1005.      */
  1006.     private Class ofClass;
  1007.     
  1008.     /* 
  1009.      * SerialVersionUID for this class.
  1010.      */
  1011.     private long suid;
  1012.     
  1013.     /*
  1014.      * The total number of bytes of primitive fields.
  1015.      * The total number of object fields.
  1016.      */
  1017.     int primBytes;
  1018.     int objFields;
  1019.  
  1020.     /* True if this class has/had a writeObject method */
  1021.     private boolean hasWriteObjectMethod;
  1022.  
  1023.     /* In JDK 1.1, external data was not written in block mode.
  1024.      * As of JDK 1.2, external data is written in block data mode. This
  1025.      * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
  1026.      *
  1027.      * @since JDK 1.2
  1028.      */
  1029.     private boolean hasExternalizableBlockData;
  1030.     Method writeObjectMethod;
  1031.     Method readObjectMethod;
  1032.  
  1033.     /*
  1034.      * ObjectStreamClass that this one was built from.
  1035.      */
  1036.     private ObjectStreamClass localClassDesc;
  1037.     
  1038.     /* Find out if the class has a static class initializer <clinit> */
  1039.     private static native boolean hasStaticInitializer(Class cl);
  1040.  
  1041.     /* The Class Object for java.io.Serializable */
  1042.     private static Class classSerializable = null;
  1043.     private static Class classExternalizable = null;
  1044.  
  1045.     /*
  1046.      * Resolve java.io.Serializable at load time.
  1047.      */
  1048.     static {
  1049.     try {
  1050.         classSerializable = Class.forName("java.io.Serializable");
  1051.         classExternalizable = Class.forName("java.io.Externalizable");
  1052.     } catch (Throwable e) {
  1053.         System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
  1054.     }
  1055.     }
  1056.  
  1057.     /** use serialVersionUID from JDK 1.1. for interoperability */
  1058.     private static final long serialVersionUID = -6120832682080437368L;
  1059.  
  1060.     /*
  1061.      * Entries held in the Cache of known ObjectStreamClass objects.
  1062.      * Entries are chained together with the same hash value (modulo array size).
  1063.      */
  1064.     private static class ObjectStreamClassEntry extends java.lang.ref.SoftReference
  1065.     {
  1066.     ObjectStreamClassEntry(ObjectStreamClass c) {
  1067.         super(c);
  1068.     }
  1069.     ObjectStreamClassEntry next;
  1070.     }
  1071.  
  1072.     /*
  1073.      * Comparator object for Classes and Interfaces
  1074.      */
  1075.     private static Comparator compareClassByName =
  1076.         new CompareClassByName();
  1077.  
  1078.     private static class CompareClassByName implements Comparator {
  1079.     public int compare(Object o1, Object o2) {
  1080.         Class c1 = (Class)o1;
  1081.         Class c2 = (Class)o2;
  1082.         return (c1.getName()).compareTo(c2.getName());
  1083.     }
  1084.     }
  1085.  
  1086.     /*
  1087.      * Comparator object for Members, Fields, and Methods
  1088.      */
  1089.     private static Comparator compareMemberByName =
  1090.         new CompareMemberByName();
  1091.  
  1092.     private static class CompareMemberByName implements Comparator {
  1093.     public int compare(Object o1, Object o2) {
  1094.         String s1 = ((Member)o1).getName();
  1095.         String s2 = ((Member)o2).getName();
  1096.  
  1097.         if (o1 instanceof Method) {
  1098.         s1 += getSignature((Method)o1);
  1099.         s2 += getSignature((Method)o2);
  1100.         } else if (o1 instanceof Constructor) {
  1101.         s1 += getSignature((Constructor)o1);
  1102.         s2 += getSignature((Constructor)o2);
  1103.         }
  1104.         return s1.compareTo(s2);
  1105.     }
  1106.     }
  1107.  
  1108.     /* It is expensive to recompute a method or constructor signature
  1109.        many times, so compute it only once using this data structure. */
  1110.     private static class MethodSignature implements Comparator {
  1111.     Member member;
  1112.     String signature;      // cached parameter signature
  1113.  
  1114.     /* Given an array of Method or Constructor members,
  1115.        return a sorted array of the non-private members.*/
  1116.     /* A better implementation would be to implement the returned data
  1117.        structure as an insertion sorted link list.*/
  1118.     static MethodSignature[] removePrivateAndSort(Member[] m) {
  1119.         int numNonPrivate = 0;
  1120.         for (int i = 0; i < m.length; i++) {
  1121.         if (! Modifier.isPrivate(m[i].getModifiers())) {
  1122.             numNonPrivate++;
  1123.         }
  1124.         }
  1125.         MethodSignature[] cm = new MethodSignature[numNonPrivate];
  1126.         int cmi = 0;
  1127.         for (int i = 0; i < m.length; i++) {
  1128.         if (! Modifier.isPrivate(m[i].getModifiers())) {
  1129.             cm[cmi] = new MethodSignature(m[i]);
  1130.             cmi++;
  1131.         }
  1132.         }
  1133.         if (cmi > 0)
  1134.         Arrays.sort(cm, cm[0]);
  1135.         return cm;
  1136.     }
  1137.  
  1138.     /* Assumes that o1 and o2 are either both methods
  1139.        or both constructors.*/
  1140.     public int compare(Object o1, Object o2) {
  1141.         /* Arrays.sort calls compare when o1 and o2 are equal.*/
  1142.         if (o1 == o2)
  1143.         return 0;
  1144.         
  1145.         MethodSignature c1 = (MethodSignature)o1;
  1146.         MethodSignature c2 = (MethodSignature)o2;
  1147.  
  1148.         int result;
  1149.         if (isConstructor()) {
  1150.         result = c1.signature.compareTo(c2.signature);
  1151.         } else { // is a Method.
  1152.         result = c1.member.getName().compareTo(c2.member.getName());
  1153.         if (result == 0)
  1154.             result = c1.signature.compareTo(c2.signature);
  1155.         }
  1156.         return result;
  1157.     }
  1158.  
  1159.     private boolean isConstructor() {
  1160.         return member instanceof Constructor;
  1161.     }
  1162.  
  1163.     private MethodSignature(Member m) {
  1164.         member = m;
  1165.         if (isConstructor()) {
  1166.         signature = ObjectStreamClass.getSignature((Constructor)m);
  1167.         } else {
  1168.         signature = ObjectStreamClass.getSignature((Method)m);
  1169.         }
  1170.     }
  1171.     }
  1172. }
  1173.