| /* ObjectStreamClass.java -- Class used to write class information |
| about serialized objects. |
| Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package java.io; |
| |
| import gnu.java.io.NullOutputStream; |
| import gnu.java.lang.reflect.TypeSignature; |
| import gnu.java.security.action.SetAccessibleAction; |
| import gnu.java.security.provider.Gnu; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Member; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.Proxy; |
| import java.security.AccessController; |
| import java.security.DigestOutputStream; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PrivilegedAction; |
| import java.security.Security; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| |
| public class ObjectStreamClass implements Serializable |
| { |
| /** |
| * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. |
| * If <code>cl</code> is null, or is not <code>Serializable</code>, |
| * null is returned. <code>ObjectStreamClass</code>'s are memorized; |
| * later calls to this method with the same class will return the |
| * same <code>ObjectStreamClass</code> object and no recalculation |
| * will be done. |
| * |
| * @see java.io.Serializable |
| */ |
| public static ObjectStreamClass lookup(Class cl) |
| { |
| if (cl == null) |
| return null; |
| if (! (Serializable.class).isAssignableFrom(cl)) |
| return null; |
| |
| return lookupForClassObject(cl); |
| } |
| |
| /** |
| * This lookup for internal use by ObjectOutputStream. Suppose |
| * we have a java.lang.Class object C for class A, though A is not |
| * serializable, but it's okay to serialize C. |
| */ |
| static ObjectStreamClass lookupForClassObject(Class cl) |
| { |
| if (cl == null) |
| return null; |
| |
| ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl); |
| |
| if (osc != null) |
| return osc; |
| else |
| { |
| osc = new ObjectStreamClass(cl); |
| classLookupTable.put(cl, osc); |
| return osc; |
| } |
| } |
| |
| /** |
| * Returns the name of the class that this |
| * <code>ObjectStreamClass</code> represents. |
| * |
| * @return the name of the class. |
| */ |
| public String getName() |
| { |
| return name; |
| } |
| |
| /** |
| * Returns the class that this <code>ObjectStreamClass</code> |
| * represents. Null could be returned if this |
| * <code>ObjectStreamClass</code> was read from an |
| * <code>ObjectInputStream</code> and the class it represents cannot |
| * be found or loaded. |
| * |
| * @see java.io.ObjectInputStream |
| */ |
| public Class forClass() |
| { |
| return clazz; |
| } |
| |
| /** |
| * Returns the serial version stream-unique identifier for the class |
| * represented by this <code>ObjectStreamClass</code>. This SUID is |
| * either defined by the class as <code>static final long |
| * serialVersionUID</code> or is calculated as specified in |
| * Javasoft's "Object Serialization Specification" XXX: add reference |
| * |
| * @return the serial version UID. |
| */ |
| public long getSerialVersionUID() |
| { |
| return uid; |
| } |
| |
| /** |
| * Returns the serializable (non-static and non-transient) Fields |
| * of the class represented by this ObjectStreamClass. The Fields |
| * are sorted by name. |
| * |
| * @return the fields. |
| */ |
| public ObjectStreamField[] getFields() |
| { |
| ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; |
| System.arraycopy(fields, 0, copy, 0, fields.length); |
| return copy; |
| } |
| |
| // XXX doc |
| // Can't do binary search since fields is sorted by name and |
| // primitiveness. |
| public ObjectStreamField getField (String name) |
| { |
| for (int i = 0; i < fields.length; i++) |
| if (fields[i].getName().equals(name)) |
| return fields[i]; |
| return null; |
| } |
| |
| /** |
| * Returns a textual representation of this |
| * <code>ObjectStreamClass</code> object including the name of the |
| * class it represents as well as that class's serial version |
| * stream-unique identifier. |
| * |
| * @see #getSerialVersionUID() |
| * @see #getName() |
| */ |
| public String toString() |
| { |
| return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; |
| } |
| |
| // Returns true iff the class that this ObjectStreamClass represents |
| // has the following method: |
| // |
| // private void writeObject (ObjectOutputStream) |
| // |
| // This method is used by the class to override default |
| // serialization behavior. |
| boolean hasWriteMethod() |
| { |
| return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; |
| } |
| |
| // Returns true iff the class that this ObjectStreamClass represents |
| // implements Serializable but does *not* implement Externalizable. |
| boolean isSerializable() |
| { |
| return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; |
| } |
| |
| |
| // Returns true iff the class that this ObjectStreamClass represents |
| // implements Externalizable. |
| boolean isExternalizable() |
| { |
| return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; |
| } |
| |
| |
| // Returns the <code>ObjectStreamClass</code> that represents the |
| // class that is the superclass of the class this |
| // <code>ObjectStreamClass</code> represents. If the superclass is |
| // not Serializable, null is returned. |
| ObjectStreamClass getSuper() |
| { |
| return superClass; |
| } |
| |
| |
| // returns an array of ObjectStreamClasses that represent the super |
| // classes of CLAZZ and CLAZZ itself in order from most super to |
| // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ |
| // that is serializable. |
| static ObjectStreamClass[] getObjectStreamClasses(Class clazz) |
| { |
| ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); |
| |
| if (osc == null) |
| return new ObjectStreamClass[0]; |
| else |
| { |
| Vector oscs = new Vector(); |
| |
| while (osc != null) |
| { |
| oscs.addElement (osc); |
| osc = osc.getSuper(); |
| } |
| |
| int count = oscs.size(); |
| ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; |
| |
| for (int i = count - 1; i >= 0; i--) |
| sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); |
| |
| return sorted_oscs; |
| } |
| } |
| |
| |
| // Returns an integer that consists of bit-flags that indicate |
| // properties of the class represented by this ObjectStreamClass. |
| // The bit-flags that could be present are those defined in |
| // ObjectStreamConstants that begin with `SC_' |
| int getFlags() |
| { |
| return flags; |
| } |
| |
| |
| ObjectStreamClass(String name, long uid, byte flags, |
| ObjectStreamField[] fields) |
| { |
| this.name = name; |
| this.uid = uid; |
| this.flags = flags; |
| this.fields = fields; |
| } |
| |
| /** |
| * This method builds the internal description corresponding to a Java Class. |
| * As the constructor only assign a name to the current ObjectStreamClass instance, |
| * that method sets the serial UID, chose the fields which will be serialized, |
| * and compute the position of the fields in the serialized stream. |
| * |
| * @param cl The Java class which is used as a reference for building the descriptor. |
| * @param superClass The descriptor of the super class for this class descriptor. |
| * @throws InvalidClassException if an incompatibility between computed UID and |
| * already set UID is found. |
| */ |
| void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException |
| { |
| this.clazz = cl; |
| |
| cacheMethods(); |
| |
| long class_uid = getClassUID(cl); |
| if (uid == 0) |
| uid = class_uid; |
| else |
| { |
| // Check that the actual UID of the resolved class matches the UID from |
| // the stream. |
| if (uid != class_uid) |
| { |
| String msg = cl + |
| ": Local class not compatible: stream serialVersionUID=" |
| + uid + ", local serialVersionUID=" + class_uid; |
| throw new InvalidClassException (msg); |
| } |
| } |
| |
| isProxyClass = clazz != null && Proxy.isProxyClass(clazz); |
| this.superClass = superClass; |
| calculateOffsets(); |
| |
| try |
| { |
| ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); |
| |
| if (exportedFields == null) |
| return; |
| |
| ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; |
| int i, j, k; |
| |
| /* We now check the import fields against the exported fields. |
| * There should not be contradiction (e.g. int x and String x) |
| * but extra virtual fields can be added to the class. |
| */ |
| |
| Arrays.sort(exportedFields); |
| |
| i = 0; j = 0; k = 0; |
| while (i < fields.length && j < exportedFields.length) |
| { |
| int comp = fields[i].compareTo(exportedFields[j]); |
| |
| if (comp < 0) |
| { |
| newFieldList[k] = fields[i]; |
| fields[i].setPersistent(false); |
| fields[i].setToSet(false); |
| i++; |
| } |
| else if (comp > 0) |
| { |
| /* field not found in imported fields. We add it |
| * in the list of supported fields. |
| */ |
| newFieldList[k] = exportedFields[j]; |
| newFieldList[k].setPersistent(true); |
| newFieldList[k].setToSet(false); |
| try |
| { |
| newFieldList[k].lookupField(clazz); |
| newFieldList[k].checkFieldType(); |
| } |
| catch (NoSuchFieldException _) |
| { |
| } |
| j++; |
| } |
| else |
| { |
| try |
| { |
| exportedFields[j].lookupField(clazz); |
| exportedFields[j].checkFieldType(); |
| } |
| catch (NoSuchFieldException _) |
| { |
| } |
| |
| if (!fields[i].getType().equals(exportedFields[j].getType())) |
| throw new InvalidClassException |
| ("serialPersistentFields must be compatible with" + |
| " imported fields (about " + fields[i].getName() + ")"); |
| newFieldList[k] = fields[i]; |
| fields[i].setPersistent(true); |
| i++; |
| j++; |
| } |
| k++; |
| } |
| |
| if (i < fields.length) |
| for (;i<fields.length;i++,k++) |
| { |
| fields[i].setPersistent(false); |
| fields[i].setToSet(false); |
| newFieldList[k] = fields[i]; |
| } |
| else |
| if (j < exportedFields.length) |
| for (;j<exportedFields.length;j++,k++) |
| { |
| exportedFields[j].setPersistent(true); |
| exportedFields[j].setToSet(false); |
| newFieldList[k] = exportedFields[j]; |
| } |
| |
| fields = new ObjectStreamField[k]; |
| System.arraycopy(newFieldList, 0, fields, 0, k); |
| } |
| catch (NoSuchFieldException ignore) |
| { |
| return; |
| } |
| catch (IllegalAccessException ignore) |
| { |
| return; |
| } |
| } |
| |
| void setSuperclass (ObjectStreamClass osc) |
| { |
| superClass = osc; |
| } |
| |
| void calculateOffsets() |
| { |
| int i; |
| ObjectStreamField field; |
| primFieldSize = 0; |
| int fcount = fields.length; |
| for (i = 0; i < fcount; ++ i) |
| { |
| field = fields[i]; |
| |
| if (! field.isPrimitive()) |
| break; |
| |
| field.setOffset(primFieldSize); |
| switch (field.getTypeCode()) |
| { |
| case 'B': |
| case 'Z': |
| ++ primFieldSize; |
| break; |
| case 'C': |
| case 'S': |
| primFieldSize += 2; |
| break; |
| case 'I': |
| case 'F': |
| primFieldSize += 4; |
| break; |
| case 'D': |
| case 'J': |
| primFieldSize += 8; |
| break; |
| } |
| } |
| |
| for (objectFieldCount = 0; i < fcount; ++ i) |
| fields[i].setOffset(objectFieldCount++); |
| } |
| |
| private Method findMethod(Method[] methods, String name, Class[] params, |
| Class returnType, boolean mustBePrivate) |
| { |
| outer: |
| for (int i = 0; i < methods.length; i++) |
| { |
| final Method m = methods[i]; |
| int mods = m.getModifiers(); |
| if (Modifier.isStatic(mods) |
| || (mustBePrivate && !Modifier.isPrivate(mods))) |
| { |
| continue; |
| } |
| |
| if (m.getName().equals(name) |
| && m.getReturnType() == returnType) |
| { |
| Class[] mp = m.getParameterTypes(); |
| if (mp.length == params.length) |
| { |
| for (int j = 0; j < mp.length; j++) |
| { |
| if (mp[j] != params[j]) |
| { |
| continue outer; |
| } |
| } |
| AccessController.doPrivileged(new SetAccessibleAction(m)); |
| return m; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void cacheMethods() |
| { |
| Method[] methods = forClass().getDeclaredMethods(); |
| readObjectMethod = findMethod(methods, "readObject", |
| new Class[] { ObjectInputStream.class }, |
| Void.TYPE, true); |
| writeObjectMethod = findMethod(methods, "writeObject", |
| new Class[] { ObjectOutputStream.class }, |
| Void.TYPE, true); |
| readResolveMethod = findMethod(methods, "readResolve", |
| new Class[0], Object.class, false); |
| writeReplaceMethod = findMethod(methods, "writeReplace", |
| new Class[0], Object.class, false); |
| } |
| |
| private ObjectStreamClass(Class cl) |
| { |
| uid = 0; |
| flags = 0; |
| isProxyClass = Proxy.isProxyClass(cl); |
| |
| clazz = cl; |
| cacheMethods(); |
| name = cl.getName(); |
| setFlags(cl); |
| setFields(cl); |
| // to those class nonserializable, its uid field is 0 |
| if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) |
| uid = getClassUID(cl); |
| superClass = lookup(cl.getSuperclass()); |
| } |
| |
| |
| // Sets bits in flags according to features of CL. |
| private void setFlags(Class cl) |
| { |
| if ((java.io.Externalizable.class).isAssignableFrom(cl)) |
| flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; |
| else if ((java.io.Serializable.class).isAssignableFrom(cl)) |
| // only set this bit if CL is NOT Externalizable |
| flags |= ObjectStreamConstants.SC_SERIALIZABLE; |
| |
| if (writeObjectMethod != null) |
| flags |= ObjectStreamConstants.SC_WRITE_METHOD; |
| } |
| |
| |
| // Sets fields to be a sorted array of the serializable fields of |
| // clazz. |
| private void setFields(Class cl) |
| { |
| SetAccessibleAction setAccessible = new SetAccessibleAction(); |
| |
| if (!isSerializable() || isExternalizable()) |
| { |
| fields = NO_FIELDS; |
| return; |
| } |
| |
| try |
| { |
| final Field f = |
| cl.getDeclaredField("serialPersistentFields"); |
| setAccessible.setMember(f); |
| AccessController.doPrivileged(setAccessible); |
| int modifiers = f.getModifiers(); |
| |
| if (Modifier.isStatic(modifiers) |
| && Modifier.isFinal(modifiers) |
| && Modifier.isPrivate(modifiers)) |
| { |
| fields = getSerialPersistentFields(cl); |
| if (fields != null) |
| { |
| Arrays.sort (fields); |
| // Retrieve field reference. |
| for (int i=0; i < fields.length; i++) |
| { |
| try |
| { |
| fields[i].lookupField(cl); |
| } |
| catch (NoSuchFieldException _) |
| { |
| fields[i].setToSet(false); |
| } |
| } |
| |
| calculateOffsets(); |
| return; |
| } |
| } |
| } |
| catch (NoSuchFieldException ignore) |
| { |
| } |
| catch (IllegalAccessException ignore) |
| { |
| } |
| |
| int num_good_fields = 0; |
| Field[] all_fields = cl.getDeclaredFields(); |
| |
| int modifiers; |
| // set non-serializable fields to null in all_fields |
| for (int i = 0; i < all_fields.length; i++) |
| { |
| modifiers = all_fields[i].getModifiers(); |
| if (Modifier.isTransient(modifiers) |
| || Modifier.isStatic(modifiers)) |
| all_fields[i] = null; |
| else |
| num_good_fields++; |
| } |
| |
| // make a copy of serializable (non-null) fields |
| fields = new ObjectStreamField[ num_good_fields ]; |
| for (int from = 0, to = 0; from < all_fields.length; from++) |
| if (all_fields[from] != null) |
| { |
| final Field f = all_fields[from]; |
| setAccessible.setMember(f); |
| AccessController.doPrivileged(setAccessible); |
| fields[to] = new ObjectStreamField(all_fields[from]); |
| to++; |
| } |
| |
| Arrays.sort(fields); |
| // Make sure we don't have any duplicate field names |
| // (Sun JDK 1.4.1. throws an Internal Error as well) |
| for (int i = 1; i < fields.length; i++) |
| { |
| if(fields[i - 1].getName().equals(fields[i].getName())) |
| throw new InternalError("Duplicate field " + |
| fields[i].getName() + " in class " + cl.getName()); |
| } |
| calculateOffsets(); |
| } |
| |
| // Returns the serial version UID defined by class, or if that |
| // isn't present, calculates value of serial version UID. |
| private long getClassUID(Class cl) |
| { |
| try |
| { |
| // Use getDeclaredField rather than getField, since serialVersionUID |
| // may not be public AND we only want the serialVersionUID of this |
| // class, not a superclass or interface. |
| final Field suid = cl.getDeclaredField("serialVersionUID"); |
| SetAccessibleAction setAccessible = new SetAccessibleAction(suid); |
| AccessController.doPrivileged(setAccessible); |
| int modifiers = suid.getModifiers(); |
| |
| if (Modifier.isStatic(modifiers) |
| && Modifier.isFinal(modifiers) |
| && suid.getType() == Long.TYPE) |
| return suid.getLong(null); |
| } |
| catch (NoSuchFieldException ignore) |
| { |
| } |
| catch (IllegalAccessException ignore) |
| { |
| } |
| |
| // cl didn't define serialVersionUID, so we have to compute it |
| try |
| { |
| MessageDigest md; |
| try |
| { |
| md = MessageDigest.getInstance("SHA"); |
| } |
| catch (NoSuchAlgorithmException e) |
| { |
| // If a provider already provides SHA, use it; otherwise, use this. |
| Gnu gnuProvider = new Gnu(); |
| Security.addProvider(gnuProvider); |
| md = MessageDigest.getInstance("SHA"); |
| } |
| |
| DigestOutputStream digest_out = |
| new DigestOutputStream(nullOutputStream, md); |
| DataOutputStream data_out = new DataOutputStream(digest_out); |
| |
| data_out.writeUTF(cl.getName()); |
| |
| int modifiers = cl.getModifiers(); |
| // just look at interesting bits |
| modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL |
| | Modifier.INTERFACE | Modifier.PUBLIC); |
| data_out.writeInt(modifiers); |
| |
| // Pretend that an array has no interfaces, because when array |
| // serialization was defined (JDK 1.1), arrays didn't have it. |
| if (! cl.isArray()) |
| { |
| Class[] interfaces = cl.getInterfaces(); |
| Arrays.sort(interfaces, interfaceComparator); |
| for (int i = 0; i < interfaces.length; i++) |
| data_out.writeUTF(interfaces[i].getName()); |
| } |
| |
| Field field; |
| Field[] fields = cl.getDeclaredFields(); |
| Arrays.sort(fields, memberComparator); |
| for (int i = 0; i < fields.length; i++) |
| { |
| field = fields[i]; |
| modifiers = field.getModifiers(); |
| if (Modifier.isPrivate(modifiers) |
| && (Modifier.isStatic(modifiers) |
| || Modifier.isTransient(modifiers))) |
| continue; |
| |
| data_out.writeUTF(field.getName()); |
| data_out.writeInt(modifiers); |
| data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); |
| } |
| |
| // write class initializer method if present |
| if (VMObjectStreamClass.hasClassInitializer(cl)) |
| { |
| data_out.writeUTF("<clinit>"); |
| data_out.writeInt(Modifier.STATIC); |
| data_out.writeUTF("()V"); |
| } |
| |
| Constructor constructor; |
| Constructor[] constructors = cl.getDeclaredConstructors(); |
| Arrays.sort (constructors, memberComparator); |
| for (int i = 0; i < constructors.length; i++) |
| { |
| constructor = constructors[i]; |
| modifiers = constructor.getModifiers(); |
| if (Modifier.isPrivate(modifiers)) |
| continue; |
| |
| data_out.writeUTF("<init>"); |
| data_out.writeInt(modifiers); |
| |
| // the replacement of '/' with '.' was needed to make computed |
| // SUID's agree with those computed by JDK |
| data_out.writeUTF |
| (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); |
| } |
| |
| Method method; |
| Method[] methods = cl.getDeclaredMethods(); |
| Arrays.sort(methods, memberComparator); |
| for (int i = 0; i < methods.length; i++) |
| { |
| method = methods[i]; |
| modifiers = method.getModifiers(); |
| if (Modifier.isPrivate(modifiers)) |
| continue; |
| |
| data_out.writeUTF(method.getName()); |
| data_out.writeInt(modifiers); |
| |
| // the replacement of '/' with '.' was needed to make computed |
| // SUID's agree with those computed by JDK |
| data_out.writeUTF |
| (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); |
| } |
| |
| data_out.close(); |
| byte[] sha = md.digest(); |
| long result = 0; |
| int len = sha.length < 8 ? sha.length : 8; |
| for (int i = 0; i < len; i++) |
| result += (long) (sha[i] & 0xFF) << (8 * i); |
| |
| return result; |
| } |
| catch (NoSuchAlgorithmException e) |
| { |
| throw new RuntimeException |
| ("The SHA algorithm was not found to use in computing the Serial Version UID for class " |
| + cl.getName(), e); |
| } |
| catch (IOException ioe) |
| { |
| throw new RuntimeException(ioe); |
| } |
| } |
| |
| /** |
| * Returns the value of CLAZZ's private static final field named |
| * `serialPersistentFields'. It performs some sanity checks before |
| * returning the real array. Besides, the returned array is a clean |
| * copy of the original. So it can be modified. |
| * |
| * @param clazz Class to retrieve 'serialPersistentFields' from. |
| * @return The content of 'serialPersistentFields'. |
| */ |
| private ObjectStreamField[] getSerialPersistentFields(Class clazz) |
| throws NoSuchFieldException, IllegalAccessException |
| { |
| ObjectStreamField[] fieldsArray = null; |
| ObjectStreamField[] o; |
| |
| // Use getDeclaredField rather than getField for the same reason |
| // as above in getDefinedSUID. |
| Field f = clazz.getDeclaredField("serialPersistentFields"); |
| f.setAccessible(true); |
| |
| int modifiers = f.getModifiers(); |
| if (!(Modifier.isStatic(modifiers) && |
| Modifier.isFinal(modifiers) && |
| Modifier.isPrivate(modifiers))) |
| return null; |
| |
| o = (ObjectStreamField[]) f.get(null); |
| |
| if (o == null) |
| return null; |
| |
| fieldsArray = new ObjectStreamField[ o.length ]; |
| System.arraycopy(o, 0, fieldsArray, 0, o.length); |
| |
| return fieldsArray; |
| } |
| |
| /** |
| * Returns a new instance of the Class this ObjectStreamClass corresponds |
| * to. |
| * Note that this should only be used for Externalizable classes. |
| * |
| * @return A new instance. |
| */ |
| Externalizable newInstance() throws InvalidClassException |
| { |
| synchronized(this) |
| { |
| if (constructor == null) |
| { |
| try |
| { |
| final Constructor c = clazz.getConstructor(new Class[0]); |
| |
| AccessController.doPrivileged(new PrivilegedAction() |
| { |
| public Object run() |
| { |
| c.setAccessible(true); |
| return null; |
| } |
| }); |
| |
| constructor = c; |
| } |
| catch(NoSuchMethodException x) |
| { |
| throw new InvalidClassException(clazz.getName(), |
| "No public zero-argument constructor"); |
| } |
| } |
| } |
| |
| try |
| { |
| return (Externalizable)constructor.newInstance(null); |
| } |
| catch(Exception x) |
| { |
| throw (InvalidClassException) |
| new InvalidClassException(clazz.getName(), |
| "Unable to instantiate").initCause(x); |
| } |
| } |
| |
| public static final ObjectStreamField[] NO_FIELDS = {}; |
| |
| private static Hashtable classLookupTable = new Hashtable(); |
| private static final NullOutputStream nullOutputStream = new NullOutputStream(); |
| private static final Comparator interfaceComparator = new InterfaceComparator(); |
| private static final Comparator memberComparator = new MemberComparator(); |
| private static final |
| Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; |
| |
| private ObjectStreamClass superClass; |
| private Class clazz; |
| private String name; |
| private long uid; |
| private byte flags; |
| |
| // this field is package protected so that ObjectInputStream and |
| // ObjectOutputStream can access it directly |
| ObjectStreamField[] fields; |
| |
| // these are accessed by ObjectIn/OutputStream |
| int primFieldSize = -1; // -1 if not yet calculated |
| int objectFieldCount; |
| |
| Method readObjectMethod; |
| Method readResolveMethod; |
| Method writeReplaceMethod; |
| Method writeObjectMethod; |
| boolean realClassIsSerializable; |
| boolean realClassIsExternalizable; |
| ObjectStreamField[] fieldMapping; |
| Constructor firstNonSerializableParentConstructor; |
| private Constructor constructor; // default constructor for Externalizable |
| |
| boolean isProxyClass = false; |
| |
| // This is probably not necessary because this class is special cased already |
| // but it will avoid showing up as a discrepancy when comparing SUIDs. |
| private static final long serialVersionUID = -6120832682080437368L; |
| |
| |
| // interfaces are compared only by name |
| private static final class InterfaceComparator implements Comparator |
| { |
| public int compare(Object o1, Object o2) |
| { |
| return ((Class) o1).getName().compareTo(((Class) o2).getName()); |
| } |
| } |
| |
| |
| // Members (Methods and Constructors) are compared first by name, |
| // conflicts are resolved by comparing type signatures |
| private static final class MemberComparator implements Comparator |
| { |
| public int compare(Object o1, Object o2) |
| { |
| Member m1 = (Member) o1; |
| Member m2 = (Member) o2; |
| |
| int comp = m1.getName().compareTo(m2.getName()); |
| |
| if (comp == 0) |
| return TypeSignature.getEncodingOfMember(m1). |
| compareTo(TypeSignature.getEncodingOfMember(m2)); |
| else |
| return comp; |
| } |
| } |
| } |