--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/corba/src/share/classes/com/sun/corba/se/impl/orbutil/ObjectStreamClass_1_3_1.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1277 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+/*
+ * Licensed Materials - Property of IBM
+ * RMI-IIOP v1.0
+ * Copyright IBM Corp. 1998 1999 All Rights Reserved
+ *
+ */
+
+package com.sun.corba.se.impl.orbutil;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.DigestOutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedAction;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.InvocationTargetException;
+
+import java.io.IOException;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidClassException;
+import java.io.Serializable;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Hashtable;
+
+import org.omg.CORBA.ValueMember;
+
+import com.sun.corba.se.impl.io.ValueUtility;
+import com.sun.corba.se.impl.io.ObjectStreamClass;
+
+/**
+ * This is duplicated here to preserve the JDK 1.3.1FCS behavior
+ * of calculating the OMG hash code incorrectly when serialPersistentFields
+ * is used, but some of the fields no longer exist in the class itself.
+ *
+ * We have to duplicate it since we aren't allowed to modify the
+ * com.sun.corba.se.impl.io version further, and can't make it
+ * public outside of its package for security reasons.
+ */
+/**
+ * A ObjectStreamClass_1_3_1 describes a class that can be serialized to a stream
+ * or a class that was serialized to a stream. It contains the name
+ * and the serialVersionUID of the class.
+ * <br>
+ * The ObjectStreamClass_1_3_1 for a specific class loaded in this Java VM can
+ * be found using the lookup method.
+ *
+ * @author Roger Riggs
+ * @since JDK1.1
+ */
+public class ObjectStreamClass_1_3_1 implements java.io.Serializable {
+
+ public static final long kDefaultUID = -1;
+
+ private static Object noArgsList[] = {};
+ private static Class noTypesList[] = {};
+
+ private static Hashtable translatedFields;
+
+ /** Find the descriptor for a class that can be serialized. Null
+ * is returned if the specified class does not implement
+ * java.io.Serializable or java.io.Externalizable.
+ */
+ static final ObjectStreamClass_1_3_1 lookup(Class cl)
+ {
+ ObjectStreamClass_1_3_1 desc = lookupInternal(cl);
+ if (desc.isSerializable() || desc.isExternalizable())
+ return desc;
+ return null;
+ }
+
+ /*
+ * Find the class descriptor for the specified class.
+ * Package access only so it can be called from ObjectIn/OutStream.
+ */
+ static ObjectStreamClass_1_3_1 lookupInternal(Class cl)
+ {
+ /* Synchronize on the hashtable so no two threads will do
+ * this at the same time.
+ */
+ ObjectStreamClass_1_3_1 desc = null;
+ synchronized (descriptorFor) {
+ /* Find the matching descriptor if it already known */
+ desc = findDescriptorFor(cl);
+ if (desc != null) {
+ return desc;
+ }
+
+ /* Check if it's serializable */
+ boolean serializable = classSerializable.isAssignableFrom(cl);
+ /* If the class is only Serializable,
+ * lookup the descriptor for the superclass.
+ */
+ ObjectStreamClass_1_3_1 superdesc = null;
+ if (serializable) {
+ Class superclass = cl.getSuperclass();
+ if (superclass != null)
+ superdesc = lookup(superclass);
+ }
+
+ /* Check if its' externalizable.
+ * If it's Externalizable, clear the serializable flag.
+ * Only one or the other may be set in the protocol.
+ */
+ boolean externalizable = false;
+ if (serializable) {
+ externalizable =
+ ((superdesc != null) && superdesc.isExternalizable()) ||
+ classExternalizable.isAssignableFrom(cl);
+ if (externalizable) {
+ serializable = false;
+ }
+ }
+
+ /* Create a new version descriptor,
+ * it put itself in the known table.
+ */
+ desc = new ObjectStreamClass_1_3_1(cl, superdesc,
+ serializable, externalizable);
+ }
+ desc.init();
+ return desc;
+ }
+
+ /**
+ * The name of the class described by this descriptor.
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Return the serialVersionUID for this class.
+ * The serialVersionUID defines a set of classes all with the same name
+ * that have evolved from a common root class and agree to be serialized
+ * and deserialized using a common format.
+ */
+ public static final long getSerialVersionUID( java.lang.Class clazz) {
+ ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
+ if( theosc != null )
+ {
+ return theosc.getSerialVersionUID( );
+ }
+ return 0;
+ }
+
+ /**
+ * Return the serialVersionUID for this class.
+ * The serialVersionUID defines a set of classes all with the same name
+ * that have evolved from a common root class and agree to be serialized
+ * and deserialized using a common format.
+ */
+ public final long getSerialVersionUID() {
+ return suid;
+ }
+
+ /**
+ * Return the serialVersionUID string for this class.
+ * The serialVersionUID defines a set of classes all with the same name
+ * that have evolved from a common root class and agree to be serialized
+ * and deserialized using a common format.
+ */
+ public final String getSerialVersionUIDStr() {
+ if (suidStr == null)
+ suidStr = Long.toHexString(suid).toUpperCase();
+ return suidStr;
+ }
+
+ /**
+ * Return the actual (computed) serialVersionUID for this class.
+ */
+ public static final long getActualSerialVersionUID( java.lang.Class clazz )
+ {
+ ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
+ if( theosc != null )
+ {
+ return theosc.getActualSerialVersionUID( );
+ }
+ return 0;
+ }
+
+ /**
+ * Return the actual (computed) serialVersionUID for this class.
+ */
+ public final long getActualSerialVersionUID() {
+ return actualSuid;
+ }
+
+ /**
+ * Return the actual (computed) serialVersionUID for this class.
+ */
+ public final String getActualSerialVersionUIDStr() {
+ if (actualSuidStr == null)
+ actualSuidStr = Long.toHexString(actualSuid).toUpperCase();
+ return actualSuidStr;
+ }
+
+ /**
+ * Return the class in the local VM that this version is mapped to.
+ * Null is returned if there is no corresponding local class.
+ */
+ public final Class forClass() {
+ return ofClass;
+ }
+
+ /**
+ * Return an array of the fields of this serializable class.
+ * @return an array containing an element for each persistent
+ * field of this class. Returns an array of length zero if
+ * there are no fields.
+ * @since JDK1.2
+ */
+ public ObjectStreamField[] getFields() {
+ // Return a copy so the caller can't change the fields.
+ if (fields.length > 0) {
+ ObjectStreamField[] dup = new ObjectStreamField[fields.length];
+ System.arraycopy(fields, 0, dup, 0, fields.length);
+ return dup;
+ } else {
+ return fields;
+ }
+ }
+
+ public boolean hasField(ValueMember field){
+
+ for (int i = 0; i < fields.length; i++){
+ try{
+ if (fields[i].getName().equals(field.name)) {
+
+ if (fields[i].getSignature().equals(ValueUtility.getSignature(field)))
+ return true;
+ }
+ }
+ catch(Throwable t){}
+ }
+ return false;
+ }
+
+ /* Avoid unnecessary allocations. */
+ final ObjectStreamField[] getFieldsNoCopy() {
+ return fields;
+ }
+
+ /**
+ * Get the field of this class by name.
+ * @return The ObjectStreamField object of the named field or null if there
+ * is no such named field.
+ */
+ public final ObjectStreamField getField(String name) {
+ /* Binary search of fields by name.
+ */
+ for (int i = fields.length-1; i >= 0; i--) {
+ if (name.equals(fields[i].getName())) {
+ return fields[i];
+ }
+ }
+ return null;
+ }
+
+ public Serializable writeReplace(Serializable value) {
+ if (writeReplaceObjectMethod != null) {
+ try {
+ return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList);
+ }
+ catch(Throwable t) {
+ throw new RuntimeException(t.getMessage());
+ }
+ }
+ else return value;
+ }
+
+ public Object readResolve(Object value) {
+ if (readResolveObjectMethod != null) {
+ try {
+ return readResolveObjectMethod.invoke(value,noArgsList);
+ }
+ catch(Throwable t) {
+ throw new RuntimeException(t.getMessage());
+ }
+ }
+ else return value;
+ }
+
+ /**
+ * Return a string describing this ObjectStreamClass_1_3_1.
+ */
+ public final String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(name);
+ sb.append(": static final long serialVersionUID = ");
+ sb.append(Long.toString(suid));
+ sb.append("L;");
+ return sb.toString();
+ }
+
+ /*
+ * Create a new ObjectStreamClass_1_3_1 from a loaded class.
+ * Don't call this directly, call lookup instead.
+ */
+ private ObjectStreamClass_1_3_1(java.lang.Class cl, ObjectStreamClass_1_3_1 superdesc,
+ boolean serial, boolean extern)
+ {
+ ofClass = cl; /* created from this class */
+
+ if (Proxy.isProxyClass(cl)) {
+ forProxyClass = true;
+ }
+
+ name = cl.getName();
+ superclass = superdesc;
+ serializable = serial;
+ if (!forProxyClass) {
+ // proxy classes are never externalizable
+ externalizable = extern;
+ }
+
+ /*
+ * Enter this class in the table of known descriptors.
+ * Otherwise, when the fields are read it may recurse
+ * trying to find the descriptor for itself.
+ */
+ insertDescriptorFor(this);
+
+ /*
+ * The remainder of initialization occurs in init(), which is called
+ * after the lock on the global class descriptor table has been
+ * released.
+ */
+ }
+
+ /*
+ * Initialize class descriptor. This method is only invoked on class
+ * descriptors created via calls to lookupInternal(). This method is kept
+ * separate from the ObjectStreamClass_1_3_1 constructor so that lookupInternal
+ * does not have to hold onto a global class descriptor table lock while the
+ * class descriptor is being initialized (see bug 4165204).
+ */
+
+
+ private void init() {
+ synchronized (lock) {
+
+ final Class cl = ofClass;
+
+ if (fields != null) // already initialized
+ return;
+
+
+ if (!serializable ||
+ externalizable ||
+ forProxyClass ||
+ name.equals("java.lang.String")) {
+ fields = NO_FIELDS;
+ } else if (serializable) {
+
+ /* Ask for permission to override field access checks.
+ */
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ /* Fill in the list of persistent fields.
+ * If it is declared, use the declared serialPersistentFields.
+ * Otherwise, extract the fields from the class itself.
+ */
+ try {
+ Field pf = cl.getDeclaredField("serialPersistentFields");
+ // serial bug 7; the serialPersistentFields were not
+ // being read and stored as Accessible bit was not set
+ pf.setAccessible(true);
+ // serial bug 7; need to find if the field is of type
+ // java.io.ObjectStreamField
+ java.io.ObjectStreamField[] f =
+ (java.io.ObjectStreamField[])pf.get(cl);
+ int mods = pf.getModifiers();
+ if ((Modifier.isPrivate(mods)) &&
+ (Modifier.isStatic(mods)) &&
+ (Modifier.isFinal(mods)))
+ {
+ fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl));
+ }
+ } catch (NoSuchFieldException e) {
+ fields = null;
+ } catch (IllegalAccessException e) {
+ fields = null;
+ } catch (IllegalArgumentException e) {
+ fields = null;
+ } catch (ClassCastException e) {
+ /* Thrown if a field serialPersistentField exists
+ * but it is not of type ObjectStreamField.
+ */
+ fields = null;
+ }
+
+
+ if (fields == null) {
+ /* Get all of the declared fields for this
+ * Class. setAccessible on all fields so they
+ * can be accessed later. Create a temporary
+ * ObjectStreamField array to hold each
+ * non-static, non-transient field. Then copy the
+ * temporary array into an array of the correct
+ * size once the number of fields is known.
+ */
+ Field[] actualfields = cl.getDeclaredFields();
+
+ int numFields = 0;
+ ObjectStreamField[] tempFields =
+ new ObjectStreamField[actualfields.length];
+ for (int i = 0; i < actualfields.length; i++) {
+ int modifiers = actualfields[i].getModifiers();
+ if (!Modifier.isStatic(modifiers) &&
+ !Modifier.isTransient(modifiers)) {
+ tempFields[numFields++] =
+ new ObjectStreamField(actualfields[i]);
+ }
+ }
+ fields = new ObjectStreamField[numFields];
+ System.arraycopy(tempFields, 0, fields, 0, numFields);
+
+ } else {
+ // For each declared persistent field, look for an actual
+ // reflected Field. If there is one, make sure it's the correct
+ // type and cache it in the ObjectStreamClass_1_3_1 for that field.
+ for (int j = fields.length-1; j >= 0; j--) {
+ try {
+ Field reflField = cl.getDeclaredField(fields[j].getName());
+ if (fields[j].getType() == reflField.getType()) {
+ // reflField.setAccessible(true);
+ fields[j].setField(reflField);
+ }
+ } catch (NoSuchFieldException e) {
+ // Nothing to do
+ }
+ }
+ }
+ return null;
+ }
+ });
+
+ if (fields.length > 1)
+ Arrays.sort(fields);
+
+ /* Set up field data for use while writing using the API api. */
+ computeFieldInfo();
+ }
+
+ /* Get the serialVersionUID from the class.
+ * It uses the access override mechanism so make sure
+ * the field objects is only used here.
+ *
+ * NonSerializable classes have a serialVerisonUID of 0L.
+ */
+ if (isNonSerializable()) {
+ suid = 0L;
+ } else {
+ // Lookup special Serializable members using reflection.
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ if (forProxyClass) {
+ // proxy classes always have serialVersionUID of 0L
+ suid = 0L;
+ } else {
+ try {
+ final Field f = cl.getDeclaredField("serialVersionUID");
+ int mods = f.getModifiers();
+ // SerialBug 5: static final SUID should be read
+ if (Modifier.isStatic(mods) &&
+ Modifier.isFinal(mods) ) {
+ f.setAccessible(true);
+ suid = f.getLong(cl);
+ // get rid of native code
+ // suid = getSerialVersionUIDField(cl);
+ // SerialBug 2: should be computed after writeObject
+ // actualSuid = computeStructuralUID(cl);
+ } else {
+ suid = ObjectStreamClass.getSerialVersionUID(cl);
+ // SerialBug 2: should be computed after writeObject
+ // actualSuid = computeStructuralUID(cl);
+ }
+ } catch (NoSuchFieldException ex) {
+ suid = ObjectStreamClass.getSerialVersionUID(cl);
+ // SerialBug 2: should be computed after writeObject
+ // actualSuid = computeStructuralUID(cl);
+ } catch (IllegalAccessException ex) {
+ suid = ObjectStreamClass.getSerialVersionUID(cl);
+ }
+ }
+
+
+ try {
+ writeReplaceObjectMethod = cl.getDeclaredMethod("writeReplace", noTypesList);
+ if (Modifier.isStatic(writeReplaceObjectMethod.getModifiers())) {
+ writeReplaceObjectMethod = null;
+ } else {
+ writeReplaceObjectMethod.setAccessible(true);
+ }
+
+ } catch (NoSuchMethodException e2) {
+
+ }
+
+ try {
+ readResolveObjectMethod = cl.getDeclaredMethod("readResolve", noTypesList);
+ if (Modifier.isStatic(readResolveObjectMethod.getModifiers())) {
+ readResolveObjectMethod = null;
+ } else {
+ readResolveObjectMethod.setAccessible(true);
+ }
+
+ } catch (NoSuchMethodException e2) {
+
+ }
+
+ /* Cache lookup of writeObject and readObject for
+ * Serializable classes. (Do not lookup for
+ * Externalizable)
+ */
+
+ if (serializable && !forProxyClass) {
+
+ /* Look for the writeObject method
+ * Set the accessible flag on it here. ObjectOutputStream
+ * will call it as necessary.
+ */
+ try {
+ Class[] args = {java.io.ObjectOutputStream.class};
+ writeObjectMethod = cl.getDeclaredMethod("writeObject", args);
+ hasWriteObjectMethod = true;
+ int mods = writeObjectMethod.getModifiers();
+
+ // Method must be private and non-static
+ if (!Modifier.isPrivate(mods) ||
+ Modifier.isStatic(mods)) {
+ writeObjectMethod = null;
+ hasWriteObjectMethod = false;
+ }
+
+ } catch (NoSuchMethodException e) {
+ }
+
+ /* Look for the readObject method
+ * set the access override and save the reference for
+ * ObjectInputStream so it can all the method directly.
+ */
+ try {
+ Class[] args = {java.io.ObjectInputStream.class};
+ readObjectMethod = cl.getDeclaredMethod("readObject", args);
+ int mods = readObjectMethod.getModifiers();
+
+ // Method must be private and non-static
+ if (!Modifier.isPrivate(mods) ||
+ Modifier.isStatic(mods)) {
+ readObjectMethod = null;
+ }
+ } catch (NoSuchMethodException e) {
+ }
+ // Compute the structural UID. This must be done after the
+ // calculation for writeObject. Fixed 4/20/2000, eea1
+ // SerialBug 2: to have correct value in RepId
+ }
+ return null;
+ }
+ });
+ }
+
+ actualSuid = computeStructuralUID(this, cl);
+ }
+
+ }
+
+ /*
+ * Create an empty ObjectStreamClass_1_3_1 for a class about to be read.
+ * This is separate from read so ObjectInputStream can assign the
+ * wire handle early, before any nested ObjectStreamClass_1_3_1 might
+ * be read.
+ */
+ ObjectStreamClass_1_3_1(String n, long s) {
+ name = n;
+ suid = s;
+ superclass = null;
+ }
+
+ private static Object[] translateFields(Object objs[])
+ throws NoSuchFieldException {
+ try{
+ java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs;
+ Object translation[] = null;
+
+ if (translatedFields == null)
+ translatedFields = new Hashtable();
+
+ translation = (Object[])translatedFields.get(fields);
+
+ if (translation != null)
+ return translation;
+ else {
+ Class osfClass = com.sun.corba.se.impl.orbutil.ObjectStreamField.class;
+
+ translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length);
+ Object arg[] = new Object[2];
+ Class types[] = {String.class, Class.class};
+ Constructor constructor = osfClass.getDeclaredConstructor(types);
+ for (int i = fields.length -1; i >= 0; i--){
+ arg[0] = fields[i].getName();
+ arg[1] = fields[i].getType();
+
+ translation[i] = constructor.newInstance(arg);
+ }
+ translatedFields.put(fields, translation);
+
+ }
+
+ return (Object[])translation;
+ }
+ catch(Throwable t){
+ throw new NoSuchFieldException();
+ }
+ }
+
+ /* Compare the base class names of streamName and localName.
+ *
+ * @return Return true iff the base class name compare.
+ * @parameter streamName Fully qualified class name.
+ * @parameter localName Fully qualified class name.
+ * @parameter pkgSeparator class names use either '.' or '/'.
+ *
+ * Only compare base class name to allow package renaming.
+ */
+ static boolean compareClassNames(String streamName,
+ String localName,
+ char pkgSeparator) {
+ /* compare the class names, stripping off package names. */
+ int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
+ if (streamNameIndex < 0)
+ streamNameIndex = 0;
+
+ int localNameIndex = localName.lastIndexOf(pkgSeparator);
+ if (localNameIndex < 0)
+ localNameIndex = 0;
+
+ return streamName.regionMatches(false, streamNameIndex,
+ localName, localNameIndex,
+ streamName.length() - streamNameIndex);
+ }
+
+ /*
+ * Compare the types of two class descriptors.
+ * They match if they have the same class name and suid
+ */
+ final boolean typeEquals(ObjectStreamClass_1_3_1 other) {
+ return (suid == other.suid) &&
+ compareClassNames(name, other.name, '.');
+ }
+
+ /*
+ * Return the superclass descriptor of this descriptor.
+ */
+ final void setSuperclass(ObjectStreamClass_1_3_1 s) {
+ superclass = s;
+ }
+
+ /*
+ * Return the superclass descriptor of this descriptor.
+ */
+ final ObjectStreamClass_1_3_1 getSuperclass() {
+ return superclass;
+ }
+
+ /*
+ * Return whether the class has a writeObject method
+ */
+ final boolean hasWriteObject() {
+ return hasWriteObjectMethod;
+ }
+
+ final boolean isCustomMarshaled() {
+ return (hasWriteObject() || isExternalizable());
+ }
+
+ /*
+ * Return true if all instances of 'this' Externalizable class
+ * are written in block-data mode from the stream that 'this' was read
+ * from. <p>
+ *
+ * In JDK 1.1, all Externalizable instances are not written
+ * in block-data mode.
+ * In JDK 1.2, all Externalizable instances, by default, are written
+ * in block-data mode and the Externalizable instance is terminated with
+ * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
+ * instances.
+ *
+ * IMPLEMENTATION NOTE:
+ * This should have been a mode maintained per stream; however,
+ * for compatibility reasons, it was only possible to record
+ * this change per class. All Externalizable classes within
+ * a given stream should either have this mode enabled or
+ * disabled. This is enforced by not allowing the PROTOCOL_VERSION
+ * of a stream to he changed after any objects have been written.
+ *
+ * @see ObjectOutputStream#useProtocolVersion
+ * @see ObjectStreamConstants#PROTOCOL_VERSION_1
+ * @see ObjectStreamConstants#PROTOCOL_VERSION_2
+ *
+ * @since JDK 1.2
+ */
+ boolean hasExternalizableBlockDataMode() {
+ return hasExternalizableBlockData;
+ }
+
+ /*
+ * Return the ObjectStreamClass_1_3_1 of the local class this one is based on.
+ */
+ final ObjectStreamClass_1_3_1 localClassDescriptor() {
+ return localClassDesc;
+ }
+
+ /*
+ * Get the Serializability of the class.
+ */
+ boolean isSerializable() {
+ return serializable;
+ }
+
+ /*
+ * Get the externalizability of the class.
+ */
+ boolean isExternalizable() {
+ return externalizable;
+ }
+
+ boolean isNonSerializable() {
+ return ! (externalizable || serializable);
+ }
+
+ /*
+ * Calculate the size of the array needed to store primitive data and the
+ * number of object references to read when reading from the input
+ * stream.
+ */
+ private void computeFieldInfo() {
+ primBytes = 0;
+ objFields = 0;
+
+ for (int i = 0; i < fields.length; i++ ) {
+ switch (fields[i].getTypeCode()) {
+ case 'B':
+ case 'Z':
+ primBytes += 1;
+ break;
+ case 'C':
+ case 'S':
+ primBytes += 2;
+ break;
+
+ case 'I':
+ case 'F':
+ primBytes += 4;
+ break;
+ case 'J':
+ case 'D' :
+ primBytes += 8;
+ break;
+
+ case 'L':
+ case '[':
+ objFields += 1;
+ break;
+ }
+ }
+ }
+
+ private static long computeStructuralUID(ObjectStreamClass_1_3_1 osc, Class cl) {
+ ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
+
+ long h = 0;
+ try {
+
+ if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
+ (cl.isInterface())){
+ return 0;
+ }
+
+ if (java.io.Externalizable.class.isAssignableFrom(cl)) {
+ return 1;
+ }
+
+ MessageDigest md = MessageDigest.getInstance("SHA");
+ DigestOutputStream mdo = new DigestOutputStream(devnull, md);
+ DataOutputStream data = new DataOutputStream(mdo);
+
+ // Get SUID of parent
+ Class parent = cl.getSuperclass();
+ if ((parent != null))
+ // SerialBug 1; acc. to spec the one for
+ // java.lang.object
+ // should be computed and put
+ // && (parent != java.lang.Object.class))
+ {
+ //data.writeLong(computeSerialVersionUID(null,parent));
+ data.writeLong(computeStructuralUID(lookup(parent), parent));
+ }
+
+ if (osc.hasWriteObject())
+ data.writeInt(2);
+ else
+ data.writeInt(1);
+
+ /* Sort the field names to get a deterministic order */
+ // Field[] field = ObjectStreamClass_1_3_1.getDeclaredFields(cl);
+
+ ObjectStreamField[] fields = osc.getFields();
+
+ // Must make sure that the Field array we allocate
+ // below is exactly the right size. Bug fix for
+ // 4397133.
+ int numNonNullFields = 0;
+ for (int i = 0; i < fields.length; i++)
+ if (fields[i].getField() != null)
+ numNonNullFields++;
+
+ Field [] field = new java.lang.reflect.Field[numNonNullFields];
+ for (int i = 0, fieldNum = 0; i < fields.length; i++) {
+ if (fields[i].getField() != null) {
+ field[fieldNum++] = fields[i].getField();
+ }
+ }
+
+ if (field.length > 1)
+ Arrays.sort(field, compareMemberByName);
+
+ for (int i = 0; i < field.length; i++) {
+ Field f = field[i];
+
+ /* Include in the hash all fields except those that are
+ * transient
+ */
+ int m = f.getModifiers();
+ //Serial 6
+ //if (Modifier.isTransient(m) || Modifier.isStatic(m))
+ // spec reference 00-01-06.pdf, 1.3.5.6, states non-static
+ // non-transient, public fields are mapped to Java IDL.
+ //
+ // Here's the quote from the first paragraph:
+ // Java non-static non-transient public fields are mapped to
+ // OMG IDL public data members, and other Java fields are
+ // not mapped.
+
+ // if (Modifier.isTransient(m) || Modifier.isStatic(m))
+ // continue;
+
+ data.writeUTF(f.getName());
+ data.writeUTF(getSignature(f.getType()));
+ }
+
+ /* Compute the hash value for this class.
+ * Use only the first 64 bits of the hash.
+ */
+ data.flush();
+ byte hasharray[] = md.digest();
+ // int minimum = Math.min(8, hasharray.length);
+ // SerialBug 3: SHA computation is wrong; for loop reversed
+ //for (int i = minimum; i > 0; i--)
+ for (int i = 0; i < Math.min(8, hasharray.length); i++) {
+ h += (long)(hasharray[i] & 255) << (i * 8);
+ }
+ } catch (IOException ignore) {
+ /* can't happen, but be deterministic anyway. */
+ h = -1;
+ } catch (NoSuchAlgorithmException complain) {
+ throw new SecurityException(complain.getMessage());
+ }
+ return h;
+ }
+
+ /**
+ * Compute the JVM signature for the class.
+ */
+ static String getSignature(Class clazz) {
+ String type = null;
+ if (clazz.isArray()) {
+ Class cl = clazz;
+ int dimensions = 0;
+ while (cl.isArray()) {
+ dimensions++;
+ cl = cl.getComponentType();
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < dimensions; i++) {
+ sb.append("[");
+ }
+ sb.append(getSignature(cl));
+ type = sb.toString();
+ } else if (clazz.isPrimitive()) {
+ if (clazz == Integer.TYPE) {
+ type = "I";
+ } else if (clazz == Byte.TYPE) {
+ type = "B";
+ } else if (clazz == Long.TYPE) {
+ type = "J";
+ } else if (clazz == Float.TYPE) {
+ type = "F";
+ } else if (clazz == Double.TYPE) {
+ type = "D";
+ } else if (clazz == Short.TYPE) {
+ type = "S";
+ } else if (clazz == Character.TYPE) {
+ type = "C";
+ } else if (clazz == Boolean.TYPE) {
+ type = "Z";
+ } else if (clazz == Void.TYPE) {
+ type = "V";
+ }
+ } else {
+ type = "L" + clazz.getName().replace('.', '/') + ";";
+ }
+ return type;
+ }
+
+ /*
+ * Compute the JVM method descriptor for the method.
+ */
+ static String getSignature(Method meth) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("(");
+
+ Class[] params = meth.getParameterTypes(); // avoid clone
+ for (int j = 0; j < params.length; j++) {
+ sb.append(getSignature(params[j]));
+ }
+ sb.append(")");
+ sb.append(getSignature(meth.getReturnType()));
+ return sb.toString();
+ }
+
+ /*
+ * Compute the JVM constructor descriptor for the constructor.
+ */
+ static String getSignature(Constructor cons) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("(");
+
+ Class[] params = cons.getParameterTypes(); // avoid clone
+ for (int j = 0; j < params.length; j++) {
+ sb.append(getSignature(params[j]));
+ }
+ sb.append(")V");
+ return sb.toString();
+ }
+
+ /*
+ * Cache of Class -> ClassDescriptor Mappings.
+ */
+ static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
+
+ /*
+ * findDescriptorFor a Class. This looks in the cache for a
+ * mapping from Class -> ObjectStreamClass mappings. The hashCode
+ * of the Class is used for the lookup since the Class is the key.
+ * The entries are extended from java.lang.ref.SoftReference so the
+ * gc will be able to free them if needed.
+ */
+ private static ObjectStreamClass_1_3_1 findDescriptorFor(Class cl) {
+
+ int hash = cl.hashCode();
+ int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
+ ObjectStreamClassEntry e;
+ ObjectStreamClassEntry prev;
+
+ /* Free any initial entries whose refs have been cleared */
+ while ((e = descriptorFor[index]) != null && e.get() == null) {
+ descriptorFor[index] = e.next;
+ }
+
+ /* Traverse the chain looking for a descriptor with ofClass == cl.
+ * unlink entries that are unresolved.
+ */
+ prev = e;
+ while (e != null ) {
+ ObjectStreamClass_1_3_1 desc = (ObjectStreamClass_1_3_1)(e.get());
+ if (desc == null) {
+ // This entry has been cleared, unlink it
+ prev.next = e.next;
+ } else {
+ if (desc.ofClass == cl)
+ return desc;
+ prev = e;
+ }
+ e = e.next;
+ }
+ return null;
+ }
+
+ /*
+ * insertDescriptorFor a Class -> ObjectStreamClass_1_3_1 mapping.
+ */
+ private static void insertDescriptorFor(ObjectStreamClass_1_3_1 desc) {
+ // Make sure not already present
+ if (findDescriptorFor(desc.ofClass) != null) {
+ return;
+ }
+
+ int hash = desc.ofClass.hashCode();
+ int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
+ ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
+ e.next = descriptorFor[index];
+ descriptorFor[index] = e;
+ }
+
+ private static Field[] getDeclaredFields(final Class clz) {
+ return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return clz.getDeclaredFields();
+ }
+ });
+ }
+
+
+ /*
+ * The name of this descriptor
+ */
+ private String name;
+
+ /*
+ * The descriptor of the supertype.
+ */
+ private ObjectStreamClass_1_3_1 superclass;
+
+ /*
+ * Flags for Serializable and Externalizable.
+ */
+ private boolean serializable;
+ private boolean externalizable;
+
+ /*
+ * Array of persistent fields of this class, sorted by
+ * type and name.
+ */
+ private ObjectStreamField[] fields;
+
+ /*
+ * Class that is a descriptor for in this virtual machine.
+ */
+ private Class ofClass;
+
+ /*
+ * True if descriptor for a proxy class.
+ */
+ boolean forProxyClass;
+
+
+ /*
+ * SerialVersionUID for this class.
+ */
+ private long suid = kDefaultUID;
+ private String suidStr = null;
+
+ /*
+ * Actual (computed) SerialVersionUID for this class.
+ */
+ private long actualSuid = kDefaultUID;
+ private String actualSuidStr = null;
+
+ /*
+ * The total number of bytes of primitive fields.
+ * The total number of object fields.
+ */
+ int primBytes;
+ int objFields;
+
+ /* Internal lock object. */
+ private Object lock = new Object();
+
+ /* True if this class has/had a writeObject method */
+ private boolean hasWriteObjectMethod;
+
+ /* In JDK 1.1, external data was not written in block mode.
+ * As of JDK 1.2, external data is written in block data mode. This
+ * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
+ *
+ * @since JDK 1.2
+ */
+ private boolean hasExternalizableBlockData;
+ Method writeObjectMethod;
+ Method readObjectMethod;
+ private Method writeReplaceObjectMethod;
+ private Method readResolveObjectMethod;
+
+ /*
+ * ObjectStreamClass_1_3_1 that this one was built from.
+ */
+ private ObjectStreamClass_1_3_1 localClassDesc;
+
+ /* Get the private static final field for serial version UID */
+ // private static native long getSerialVersionUIDField(Class cl);
+
+ /* The Class Object for java.io.Serializable */
+ private static Class classSerializable = null;
+ private static Class classExternalizable = null;
+
+ /*
+ * Resolve java.io.Serializable at load time.
+ */
+ static {
+ try {
+ classSerializable = Class.forName("java.io.Serializable");
+ classExternalizable = Class.forName("java.io.Externalizable");
+ } catch (Throwable e) {
+ System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
+ }
+ }
+
+ /** use serialVersionUID from JDK 1.1. for interoperability */
+ private static final long serialVersionUID = -6120832682080437368L;
+
+ /**
+ * Set serialPersistentFields of a Serializable class to this value to
+ * denote that the class has no Serializable fields.
+ */
+ public static final ObjectStreamField[] NO_FIELDS =
+ new ObjectStreamField[0];
+
+ /*
+ * Entries held in the Cache of known ObjectStreamClass_1_3_1 objects.
+ * Entries are chained together with the same hash value (modulo array size).
+ */
+ private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference
+ {
+ ObjectStreamClassEntry(ObjectStreamClass_1_3_1 c) {
+ //super(c);
+ this.c = c;
+ }
+ ObjectStreamClassEntry next;
+
+ public Object get()
+ {
+ return c;
+ }
+ private ObjectStreamClass_1_3_1 c;
+ }
+
+ /*
+ * Comparator object for Classes and Interfaces
+ */
+ private static Comparator compareClassByName =
+ new CompareClassByName();
+
+ private static class CompareClassByName implements Comparator {
+ public int compare(Object o1, Object o2) {
+ Class c1 = (Class)o1;
+ Class c2 = (Class)o2;
+ return (c1.getName()).compareTo(c2.getName());
+ }
+ }
+
+ /*
+ * Comparator object for Members, Fields, and Methods
+ */
+ private static Comparator compareMemberByName =
+ new CompareMemberByName();
+
+ private static class CompareMemberByName implements Comparator {
+ public int compare(Object o1, Object o2) {
+ String s1 = ((Member)o1).getName();
+ String s2 = ((Member)o2).getName();
+
+ if (o1 instanceof Method) {
+ s1 += getSignature((Method)o1);
+ s2 += getSignature((Method)o2);
+ } else if (o1 instanceof Constructor) {
+ s1 += getSignature((Constructor)o1);
+ s2 += getSignature((Constructor)o2);
+ }
+ return s1.compareTo(s2);
+ }
+ }
+
+ /* It is expensive to recompute a method or constructor signature
+ many times, so compute it only once using this data structure. */
+ private static class MethodSignature implements Comparator {
+ Member member;
+ String signature; // cached parameter signature
+
+ /* Given an array of Method or Constructor members,
+ return a sorted array of the non-private members.*/
+ /* A better implementation would be to implement the returned data
+ structure as an insertion sorted link list.*/
+ static MethodSignature[] removePrivateAndSort(Member[] m) {
+ int numNonPrivate = 0;
+ for (int i = 0; i < m.length; i++) {
+ if (! Modifier.isPrivate(m[i].getModifiers())) {
+ numNonPrivate++;
+ }
+ }
+ MethodSignature[] cm = new MethodSignature[numNonPrivate];
+ int cmi = 0;
+ for (int i = 0; i < m.length; i++) {
+ if (! Modifier.isPrivate(m[i].getModifiers())) {
+ cm[cmi] = new MethodSignature(m[i]);
+ cmi++;
+ }
+ }
+ if (cmi > 0)
+ Arrays.sort(cm, cm[0]);
+ return cm;
+ }
+
+ /* Assumes that o1 and o2 are either both methods
+ or both constructors.*/
+ public int compare(Object o1, Object o2) {
+ /* Arrays.sort calls compare when o1 and o2 are equal.*/
+ if (o1 == o2)
+ return 0;
+
+ MethodSignature c1 = (MethodSignature)o1;
+ MethodSignature c2 = (MethodSignature)o2;
+
+ int result;
+ if (isConstructor()) {
+ result = c1.signature.compareTo(c2.signature);
+ } else { // is a Method.
+ result = c1.member.getName().compareTo(c2.member.getName());
+ if (result == 0)
+ result = c1.signature.compareTo(c2.signature);
+ }
+ return result;
+ }
+
+ final private boolean isConstructor() {
+ return member instanceof Constructor;
+ }
+ private MethodSignature(Member m) {
+ member = m;
+ if (isConstructor()) {
+ signature = ObjectStreamClass_1_3_1.getSignature((Constructor)m);
+ } else {
+ signature = ObjectStreamClass_1_3_1.getSignature((Method)m);
+ }
+ }
+ }
+}