/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.io; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; import org.apache.harmony.luni.util.ThreadLocalCache; /** * Represents a descriptor for identifying a class during serialization and * deserialization. Information contained in the descriptor includes the name * and SUID of the class as well as field names and types. Information inherited * from the superclasses is also taken into account. * * @see ObjectOutputStream * @see ObjectInputStream * @see java.lang.Class */ public class ObjectStreamClass implements Serializable { // No need to compute the SUID for ObjectStreamClass, just use the value // below private static final long serialVersionUID = -6120832682080437368L; // Name of the field that contains the SUID value (if present) private static final String UID_FIELD_NAME = "serialVersionUID"; //$NON-NLS-1$ static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1; private static final int CLASS_MODIFIERS_MASK; private static final int FIELD_MODIFIERS_MASK; private static final int METHOD_MODIFIERS_MASK; private static final Class<?>[] READ_PARAM_TYPES; private static final Class<?>[] WRITE_PARAM_TYPES; static final Class<?>[] EMPTY_CONSTRUCTOR_PARAM_TYPES; private static final Class<Void> VOID_CLASS; static final Class<?>[] UNSHARED_PARAM_TYPES; private static native void oneTimeInitialization(); static { oneTimeInitialization(); CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL | Modifier.INTERFACE | Modifier.ABSTRACT; FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | Modifier.TRANSIENT; METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT; READ_PARAM_TYPES = new Class[1]; WRITE_PARAM_TYPES = new Class[1]; READ_PARAM_TYPES[0] = ObjectInputStream.class; WRITE_PARAM_TYPES[0] = ObjectOutputStream.class; EMPTY_CONSTRUCTOR_PARAM_TYPES = new Class[0]; VOID_CLASS = Void.TYPE; UNSHARED_PARAM_TYPES = new Class[1]; UNSHARED_PARAM_TYPES[0] = Object.class; } /** * Constant indicating that the class has no Serializable fields. */ public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0]; /* * used to fetch field serialPersistentFields and checking its type */ static final Class<?> ARRAY_OF_FIELDS; static { try { ARRAY_OF_FIELDS = Class.forName("[Ljava.io.ObjectStreamField;"); //$NON-NLS-1$ } catch (ClassNotFoundException e) { // This should not happen throw new AssertionError(e); } } private static final String CLINIT_NAME = "<clinit>"; //$NON-NLS-1$ private static final int CLINIT_MODIFIERS = Modifier.STATIC; private static final String CLINIT_SIGNATURE = "()V"; //$NON-NLS-1$ // Used to determine if an object is Serializable or Externalizable private static final Class<Serializable> SERIALIZABLE = Serializable.class; private static final Class<Externalizable> EXTERNALIZABLE = Externalizable.class; // Used to test if the object is a String or a class. static final Class<String> STRINGCLASS = String.class; static final Class<?> CLASSCLASS = Class.class; static final Class<ObjectStreamClass> OBJECTSTREAMCLASSCLASS = ObjectStreamClass.class; private transient Method methodWriteReplace; private transient Method methodReadResolve; private transient Method methodWriteObject; private transient Method methodReadObject; private transient Method methodReadObjectNoData; /** * Indicates whether the class properties resolved * * @see #resolveProperties() */ private transient boolean arePropertiesResolved; /** * Cached class properties * * @see #resolveProperties() * @see #isSerializable() * @see #isExternalizable() * @see #isProxy() * @see #isEnum() */ private transient boolean isSerializable; private transient boolean isExternalizable; private transient boolean isProxy; private transient boolean isEnum; // ClassDesc // // Name of the class this descriptor represents private transient String className; // Corresponding loaded class with the name above private transient WeakReference<Class<?>> resolvedClass; // Serial version UID of the class the descriptor represents private transient long svUID; // ClassDescInfo // // Any combination of SC_WRITE_METHOD, SC_SERIALIZABLE and SC_EXTERNALIZABLE // (see ObjectStreamConstants) private transient byte flags; // Descriptor for the superclass of the class associated with this // descriptor private transient ObjectStreamClass superclass; // Array of ObjectStreamField (see below) describing the fields of this // class private transient ObjectStreamField[] fields; // Array of ObjectStreamField describing the serialized fields of this class private transient ObjectStreamField[] loadFields; // MethodID for deserialization constructor private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED; void setConstructor(long newConstructor) { constructor = newConstructor; } long getConstructor() { return constructor; } /* * If an ObjectStreamClass describes an Externalizable class, it (the * descriptor) should not have field descriptors (ObjectStreamField) at all. * The ObjectStreamClass that gets saved should simply have no field info. * This is a footnote in page 1511 (class Serializable) of "The Java Class * Libraries, Second Edition, Vol. I". */ /** * Constructs a new instance of this class. */ ObjectStreamClass() { super(); } /** * Compute class descriptor for a given class <code>cl</code>. * * @param cl * a java.langClass for which to compute the corresponding * descriptor * @return the computer class descriptor */ private static ObjectStreamClass createClassDesc(Class<?> cl) { ObjectStreamClass result = new ObjectStreamClass(); boolean isArray = cl.isArray(); boolean serializable = isSerializable(cl); boolean externalizable = isExternalizable(cl); result.isSerializable = serializable; result.isExternalizable = externalizable; // Now we fill in the values result.setName(cl.getName()); result.setClass(cl); Class<?> superclass = cl.getSuperclass(); if (superclass != null) { result.setSuperclass(lookup(superclass)); } Field[] declaredFields = null; // Compute the SUID if(serializable || externalizable) { if (result.isEnum() || result.isProxy()) { result.setSerialVersionUID(0L); } else { declaredFields = cl.getDeclaredFields(); result.setSerialVersionUID(computeSerialVersionUID(cl, declaredFields)); } } // Serializables need field descriptors if (serializable && !isArray) { if (declaredFields == null) { declaredFields = cl.getDeclaredFields(); } result.buildFieldDescriptors(declaredFields); } else { // Externalizables or arrays do not need FieldDesc info result.setFields(NO_FIELDS); } // Copy all fields to loadFields - they should be read by default in // ObjectInputStream.defaultReadObject() method ObjectStreamField[] fields = result.getFields(); if (fields != null) { ObjectStreamField[] loadFields = new ObjectStreamField[fields.length]; for (int i = 0; i < fields.length; ++i) { loadFields[i] = new ObjectStreamField(fields[i].getName(), fields[i].getType(), fields[i].isUnshared()); // resolve type string to init typeString field in // ObjectStreamField loadFields[i].getTypeString(); } result.setLoadFields(loadFields); } byte flags = 0; if (externalizable) { flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default } else if (serializable) { flags |= ObjectStreamConstants.SC_SERIALIZABLE; } result.methodWriteReplace = findMethod(cl, "writeReplace"); //$NON-NLS-1$ result.methodReadResolve = findMethod(cl, "readResolve"); //$NON-NLS-1$ result.methodWriteObject = findPrivateMethod(cl, "writeObject", //$NON-NLS-1$ WRITE_PARAM_TYPES); result.methodReadObject = findPrivateMethod(cl, "readObject", //$NON-NLS-1$ READ_PARAM_TYPES); result.methodReadObjectNoData = findPrivateMethod(cl, "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES); //$NON-NLS-1$ if (result.hasMethodWriteObject()) { flags |= ObjectStreamConstants.SC_WRITE_METHOD; } result.setFlags(flags); return result; } /** * Builds the collection of field descriptors for the receiver * * @param declaredFields * collection of java.lang.reflect.Field for which to compute * field descriptors */ void buildFieldDescriptors(Field[] declaredFields) { // We could find the field ourselves in the collection, but calling // reflect is easier. Optimize if needed. final Field f = ObjectStreamClass.fieldSerialPersistentFields(this .forClass()); // If we could not find the emulated fields, we'll have to compute // dumpable fields from reflect fields boolean useReflectFields = f == null; // Assume we will compute the // fields to dump based on the // reflect fields ObjectStreamField[] _fields = null; if (!useReflectFields) { // The user declared a collection of emulated fields. Use them. // We have to be able to fetch its value, even if it is private AccessController.doPrivileged(new PriviAction<Object>(f)); try { // static field, pass null _fields = (ObjectStreamField[]) f.get(null); } catch (IllegalAccessException ex) { // WARNING - what should we do if we have no access ? This // should not happen. throw new RuntimeException(ex); } } else { // Compute collection of dumpable fields based on reflect fields List<ObjectStreamField> serializableFields = new ArrayList<ObjectStreamField>( declaredFields.length); // Filter, we are only interested in fields that are serializable for (int i = 0; i < declaredFields.length; i++) { Field declaredField = declaredFields[i]; int modifiers = declaredField.getModifiers(); boolean shouldBeSerialized = !(Modifier.isStatic(modifiers) || Modifier .isTransient(modifiers)); if (shouldBeSerialized) { ObjectStreamField field = new ObjectStreamField( declaredField.getName(), declaredField.getType()); serializableFields.add(field); } } if (serializableFields.size() == 0) { _fields = NO_FIELDS; // If no serializable fields, share the // special value so that users can test } else { // Now convert from Vector to array _fields = new ObjectStreamField[serializableFields.size()]; _fields = serializableFields.toArray(_fields); } } ObjectStreamField.sortFields(_fields); // assign offsets int primOffset = 0, objectOffset = 0; for (int i = 0; i < _fields.length; i++) { Class<?> type = _fields[i].getType(); if (type.isPrimitive()) { _fields[i].offset = primOffset; primOffset += primitiveSize(type); } else { _fields[i].offset = objectOffset++; } } fields = _fields; } /** * Compute and return the Serial Version UID of the class {@code cl}. * The value is computed based on the class name, superclass chain, field * names, method names, modifiers, etc. * * @param cl * a java.lang.Class for which to compute the SUID * @param fields * cl.getDeclaredFields(), pre-computed by the caller * @return the value of SUID of this class */ private static long computeSerialVersionUID(Class<?> cl, Field[] fields) { /* * First we should try to fetch the static slot 'static final long * serialVersionUID'. If it is defined, return it. If not defined, we * really need to compute SUID using SHAOutputStream */ for (int i = 0; i < fields.length; i++) { final Field field = fields[i]; if (Long.TYPE == field.getType()) { int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { if (UID_FIELD_NAME.equals(field.getName())) { /* * We need to be able to see it even if we have no * visibility. That is why we set accessible first (new * API in reflect 1.2) */ AccessController.doPrivileged(new PriviAction<Object>( field)); try { // Static field, parameter is ignored return field.getLong(null); } catch (IllegalAccessException iae) { throw new RuntimeException(Msg.getString( "K0071", iae)); //$NON-NLS-1$ } } } } } MessageDigest digest; try { digest = MessageDigest.getInstance("SHA"); //$NON-NLS-1$ } catch (NoSuchAlgorithmException e) { throw new Error(e); } ByteArrayOutputStream sha = new ByteArrayOutputStream(); try { DataOutputStream output = new DataOutputStream(sha); output.writeUTF(cl.getName()); int classModifiers = CLASS_MODIFIERS_MASK & cl.getModifiers(); /* * Workaround for 1F9LOQO. Arrays are ABSTRACT in JDK, but that is * not in the specification. Since we want to be compatible for * X-loading, we have to pretend we have the same shape */ boolean isArray = cl.isArray(); if (isArray) { classModifiers |= Modifier.ABSTRACT; } // Required for JDK UID compatibility if (cl.isInterface() && !Modifier.isPublic(classModifiers)) { classModifiers &= ~Modifier.ABSTRACT; } output.writeInt(classModifiers); /* * In JDK1.2 arrays implement Cloneable and Serializable but not in * JDK 1.1.7. So, JDK 1.2 "pretends" arrays have no interfaces when * computing SHA-1 to be compatible. */ if (!isArray) { // Interface information Class<?>[] interfaces = cl.getInterfaces(); if (interfaces.length > 1) { // Only attempt to sort if really needed (saves object // creation, etc) Comparator<Class<?>> interfaceComparator = new Comparator<Class<?>>() { public int compare(Class<?> itf1, Class<?> itf2) { return itf1.getName().compareTo(itf2.getName()); } }; Arrays.sort(interfaces, interfaceComparator); } // Dump them for (int i = 0; i < interfaces.length; i++) { output.writeUTF(interfaces[i].getName()); } } // Field information if (fields.length > 1) { // Only attempt to sort if really needed (saves object creation, // etc) Comparator<Field> fieldComparator = new Comparator<Field>() { public int compare(Field field1, Field field2) { return field1.getName().compareTo(field2.getName()); } }; Arrays.sort(fields, fieldComparator); } // Dump them for (int i = 0; i < fields.length; i++) { Field field = fields[i]; int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK; boolean skip = Modifier.isPrivate(modifiers) && (Modifier.isTransient(modifiers) || Modifier .isStatic(modifiers)); if (!skip) { // write name, modifier & "descriptor" of all but private // static and private transient output.writeUTF(field.getName()); output.writeInt(modifiers); output .writeUTF(descriptorForFieldSignature(getFieldSignature(field))); } } /* * Normally constructors come before methods (because <init> < * anyMethodName). However, <clinit> is an exception. Besides, * reflect will not let us get to it. */ if (hasClinit(cl)) { // write name, modifier & "descriptor" output.writeUTF(CLINIT_NAME); output.writeInt(CLINIT_MODIFIERS); output.writeUTF(CLINIT_SIGNATURE); } // Constructor information Constructor<?>[] constructors = cl.getDeclaredConstructors(); if (constructors.length > 1) { // Only attempt to sort if really needed (saves object creation, // etc) Comparator<Constructor<?>> constructorComparator = new Comparator<Constructor<?>>() { public int compare(Constructor<?> ctr1, Constructor<?> ctr2) { // All constructors have same name, so we sort based on // signature return (getConstructorSignature(ctr1) .compareTo(getConstructorSignature(ctr2))); } }; Arrays.sort(constructors, constructorComparator); } // Dump them for (int i = 0; i < constructors.length; i++) { Constructor<?> constructor = constructors[i]; int modifiers = constructor.getModifiers() & METHOD_MODIFIERS_MASK; boolean isPrivate = Modifier.isPrivate(modifiers); if (!isPrivate) { /* * write name, modifier & "descriptor" of all but private * ones * * constructor.getName() returns the constructor name as * typed, not the VM name */ output.writeUTF("<init>"); //$NON-NLS-1$ output.writeInt(modifiers); output.writeUTF(descriptorForSignature( getConstructorSignature(constructor)).replace('/', '.')); } } // Method information Method[] methods = cl.getDeclaredMethods(); if (methods.length > 1) { Comparator<Method> methodComparator = new Comparator<Method>() { public int compare(Method m1, Method m2) { int result = m1.getName().compareTo(m2.getName()); if (result == 0) { // same name, signature will tell which one comes // first return getMethodSignature(m1).compareTo( getMethodSignature(m2)); } return result; } }; Arrays.sort(methods, methodComparator); } // Dump them for (int i = 0; i < methods.length; i++) { Method method = methods[i]; int modifiers = method.getModifiers() & METHOD_MODIFIERS_MASK; boolean isPrivate = Modifier.isPrivate(modifiers); if (!isPrivate) { // write name, modifier & "descriptor" of all but private // ones output.writeUTF(method.getName()); output.writeInt(modifiers); output.writeUTF(descriptorForSignature( getMethodSignature(method)).replace('/', '.')); } } } catch (IOException e) { throw new RuntimeException(Msg.getString("K0072", e));//$NON-NLS-1$ } // now compute the UID based on the SHA byte[] hash = digest.digest(sha.toByteArray()); return littleEndianLongAt(hash, 0); } /** * Returns what the serializaton specification calls "descriptor" given a * field signature. * * @param signature * a field signature * @return containing the descriptor */ private static String descriptorForFieldSignature(String signature) { return signature.replace('.', '/'); } /** * Return what the serializaton specification calls "descriptor" given a * method/constructor signature. * * @param signature * a method or constructor signature * @return containing the descriptor */ private static String descriptorForSignature(String signature) { return signature.substring(signature.indexOf("(")); //$NON-NLS-1$ } /** * Return the java.lang.reflect.Field {@code serialPersistentFields} * if class {@code cl} implements it. Return null otherwise. * * @param cl * a java.lang.Class which to test * @return {@code java.lang.reflect.Field} if the class has * serialPersistentFields {@code null} if the class does not * have serialPersistentFields */ static Field fieldSerialPersistentFields(Class<?> cl) { try { Field f = cl.getDeclaredField("serialPersistentFields"); //$NON-NLS-1$ int modifiers = f.getModifiers(); if (Modifier.isStatic(modifiers) && Modifier.isPrivate(modifiers) && Modifier.isFinal(modifiers)) { if (f.getType() == ARRAY_OF_FIELDS) { return f; } } } catch (NoSuchFieldException nsm) { // Ignored } return null; } /** * Returns the class (java.lang.Class) for this descriptor. * * @return the class in the local VM that this descriptor represents; * {@code null} if there is no corresponding class. */ public Class<?> forClass() { if (resolvedClass != null) { return resolvedClass.get(); } return null; } /** * Return a String representing the signature for a Constructor {@code c}. * * @param c * a java.lang.reflect.Constructor for which to compute the * signature * @return the constructor's signature */ static native String getConstructorSignature(Constructor<?> c); /** * Gets a field descriptor of the class represented by this class * descriptor. * * @param name * the name of the desired field. * @return the field identified by {@code name} or {@code null} if there is * no such field. */ public ObjectStreamField getField(String name) { ObjectStreamField[] allFields = getFields(); for (int i = 0; i < allFields.length; i++) { ObjectStreamField f = allFields[i]; if (f.getName().equals(name)) { return f; } } return null; } /** * Returns the collection of field descriptors for the fields of the * corresponding class * * @return the receiver's collection of declared fields for the class it * represents */ ObjectStreamField[] fields() { if (fields == null) { Class<?> forCl = forClass(); if (forCl != null && isSerializable() && !forCl.isArray()) { buildFieldDescriptors(forCl.getDeclaredFields()); } else { // Externalizables or arrays do not need FieldDesc info setFields(NO_FIELDS); } } return fields; } /** * Returns a collection of field descriptors for the serialized fields of * the class represented by this class descriptor. * * @return an array of field descriptors or an array of length zero if there * are no fields in this descriptor's class. */ public ObjectStreamField[] getFields() { copyFieldAttributes(); return loadFields == null ? fields().clone() : loadFields.clone(); } /** * If a Class uses "serialPersistentFields" to define the serialized fields, * this.loadFields cannot get the "unshared" information when deserializing * fields using current implementation of ObjectInputStream. This method * provides a way to copy the "unshared" attribute from this.fields. * */ private void copyFieldAttributes() { if ((loadFields == null) || fields == null) { return; } for (int i = 0; i < loadFields.length; i++) { ObjectStreamField loadField = loadFields[i]; String name = loadField.getName(); for (int j = 0; j < fields.length; j++) { ObjectStreamField field = fields[j]; if (name.equals(field.getName())) { loadField.setUnshared(field.isUnshared()); loadField.setOffset(field.getOffset()); break; } } } } /** * Returns the collection of field descriptors for the input fields of the * corresponding class * * @return the receiver's collection of input fields for the class it * represents */ ObjectStreamField[] getLoadFields() { return loadFields; } /** * Return a String representing the signature for a field {@code f}. * * @param f * a java.lang.reflect.Field for which to compute the signature * @return the field's signature */ private static native String getFieldSignature(Field f); /** * Returns the flags for this descriptor, where possible combined values are * * ObjectStreamConstants.SC_WRITE_METHOD * ObjectStreamConstants.SC_SERIALIZABLE * ObjectStreamConstants.SC_EXTERNALIZABLE * * @return byte the receiver's flags for the class it represents */ byte getFlags() { return flags; } /** * Return a String representing the signature for a method {@code m}. * * @param m * a java.lang.reflect.Method for which to compute the signature * @return the method's signature */ static native String getMethodSignature(Method m); /** * Returns the name of the class represented by this descriptor. * * @return the fully qualified name of the class this descriptor represents. */ public String getName() { return className; } /** * Returns the Serial Version User ID of the class represented by this * descriptor. * * @return the SUID for the class represented by this descriptor. */ public long getSerialVersionUID() { return svUID; } /** * Returns the descriptor (ObjectStreamClass) of the superclass of the class * represented by the receiver. * * @return an ObjectStreamClass representing the superclass of the class * represented by the receiver. */ ObjectStreamClass getSuperclass() { return superclass; } /** * Return true if the given class {@code cl} has the * compiler-generated method {@code clinit}. Even though it is * compiler-generated, it is used by the serialization code to compute SUID. * This is unfortunate, since it may depend on compiler optimizations in * some cases. * * @param cl * a java.lang.Class which to test * @return {@code true} if the class has <clinit> {@code false} * if the class does not have <clinit> */ private static native boolean hasClinit(Class<?> cl); /** * Return true if instances of class {@code cl} are Externalizable, * false otherwise. * * @param cl * a java.lang.Class which to test * @return {@code true} if instances of the class are Externalizable * {@code false} if instances of the class are not * Externalizable * * @see Object#hashCode */ static boolean isExternalizable(Class<?> cl) { return EXTERNALIZABLE.isAssignableFrom(cl); } /** * Return true if the type code * <code>typecode<code> describes a primitive type * * @param typecode a char describing the typecode * @return {@code true} if the typecode represents a primitive type * {@code false} if the typecode represents an Object type (including arrays) * * @see Object#hashCode */ static boolean isPrimitiveType(char typecode) { return !(typecode == '[' || typecode == 'L'); } /** * Return true if instances of class {@code cl} are Serializable, * false otherwise. * * @param cl * a java.lang.Class which to test * @return {@code true} if instances of the class are Serializable * {@code false} if instances of the class are not * Serializable * * @see Object#hashCode */ static boolean isSerializable(Class<?> cl) { return SERIALIZABLE.isAssignableFrom(cl); } /** * Resolves the class properties, if they weren't already */ private void resolveProperties() { if (arePropertiesResolved) { return; } Class<?> cl = forClass(); isProxy = Proxy.isProxyClass(cl); isEnum = Enum.class.isAssignableFrom(cl); isSerializable = isSerializable(cl); isExternalizable = isExternalizable(cl); arePropertiesResolved = true; } /** * Answers whether the class for this descriptor is serializable * * @return true if class implements Serializable */ boolean isSerializable() { resolveProperties(); return isSerializable; } /** * Answers whether the class for this descriptor is serializable * * @return true if class implements Serializable */ boolean isExternalizable() { resolveProperties(); return isExternalizable; } /** * Answers whether the class for this descriptor is proxied class * * @return true if class is proxied */ boolean isProxy() { resolveProperties(); return isProxy; } /** * Answers whether the class for this descriptor is subclass of Enum * * @return true if class is subclass of Enum */ boolean isEnum() { resolveProperties(); return isEnum; } /** * Return a little endian long stored in a given position of the buffer * * @param buffer * a byte array with the byte representation of the number * @param position * index where the number starts in the byte array * @return the number that was stored in little endian format */ private static long littleEndianLongAt(byte[] buffer, int position) { long result = 0; for (int i = position + 7; i >= position; i--) { result = (result << 8) + (buffer[i] & 0xff); } return result; } /** * Returns the descriptor corresponding to the class {@code cl}. If the * class is not serializable or externalizable then {@code null} is * returned. * * @param cl * a java.langClass for which to obtain the corresponding * descriptor * @return the corresponding descriptor if the {@code cl} is serializable or * externalizable; {@code null} otherwise. */ public static ObjectStreamClass lookup(Class<?> cl) { ObjectStreamClass osc = lookupStreamClass(cl); if (osc.isSerializable() || osc.isExternalizable()) { return osc; } return null; } /** * Return the descriptor (ObjectStreamClass) corresponding to the class * {@code cl}. Returns an ObjectStreamClass even if instances of the * class cannot be serialized * * @param cl * a java.langClass for which to obtain the corresponding * descriptor * @return the corresponding descriptor */ static ObjectStreamClass lookupStreamClass(Class<?> cl) { WeakHashMap<Class<?>,ObjectStreamClass> tlc = OSCThreadLocalCache.oscWeakHashMap.get(); ObjectStreamClass cachedValue = tlc.get(cl); if (cachedValue == null) { cachedValue = createClassDesc(cl); tlc.put(cl, cachedValue); } return cachedValue; } /** * Return the java.lang.reflect.Method if class <code>cl</code> implements * <code>methodName</code> . Return null otherwise. * * @param cl * a java.lang.Class which to test * @return <code>java.lang.reflect.Method</code> if the class implements * writeReplace <code>null</code> if the class does not implement * writeReplace */ static Method findMethod(Class<?> cl, String methodName) { Class<?> search = cl; Method method = null; while (search != null) { try { method = search.getDeclaredMethod(methodName, (Class[]) null); if (search == cl || (method.getModifiers() & Modifier.PRIVATE) == 0) { method.setAccessible(true); return method; } } catch (NoSuchMethodException nsm) { } search = search.getSuperclass(); } return null; } /** * Return the java.lang.reflect.Method if class <code>cl</code> implements * private <code>methodName</code> . Return null otherwise. * * @param cl * a java.lang.Class which to test * @return {@code java.lang.reflect.Method} if the class implements * writeReplace {@code null} if the class does not implement * writeReplace */ static Method findPrivateMethod(Class<?> cl, String methodName, Class<?>[] param) { try { Method method = cl.getDeclaredMethod(methodName, param); if (Modifier.isPrivate(method.getModifiers()) && method.getReturnType() == VOID_CLASS) { method.setAccessible(true); return method; } } catch (NoSuchMethodException nsm) { // Ignored } return null; } boolean hasMethodWriteReplace() { return (methodWriteReplace != null); } Method getMethodWriteReplace() { return methodWriteReplace; } boolean hasMethodReadResolve() { return (methodReadResolve != null); } Method getMethodReadResolve() { return methodReadResolve; } boolean hasMethodWriteObject() { return (methodWriteObject != null); } Method getMethodWriteObject() { return methodWriteObject; } boolean hasMethodReadObject() { return (methodReadObject != null); } Method getMethodReadObject() { return methodReadObject; } boolean hasMethodReadObjectNoData() { return (methodReadObjectNoData != null); } Method getMethodReadObjectNoData() { return methodReadObjectNoData; } void initPrivateFields(ObjectStreamClass desc) { methodWriteReplace = desc.methodWriteReplace; methodReadResolve = desc.methodReadResolve; methodWriteObject = desc.methodWriteObject; methodReadObject = desc.methodReadObject; methodReadObjectNoData = desc.methodReadObjectNoData; } /** * Set the class (java.lang.Class) that the receiver represents * * @param c * aClass, the new class that the receiver describes */ void setClass(Class<?> c) { resolvedClass = new WeakReference<Class<?>>(c); } /** * Set the collection of field descriptors for the fields of the * corresponding class * * @param f * ObjectStreamField[], the receiver's new collection of declared * fields for the class it represents */ void setFields(ObjectStreamField[] f) { fields = f; } /** * Set the collection of field descriptors for the input fields of the * corresponding class * * @param f * ObjectStreamField[], the receiver's new collection of input * fields for the class it represents */ void setLoadFields(ObjectStreamField[] f) { loadFields = f; } /** * Set the flags for this descriptor, where possible combined values are * * ObjectStreamConstants.SC_WRITE_METHOD * ObjectStreamConstants.SC_SERIALIZABLE * ObjectStreamConstants.SC_EXTERNALIZABLE * * @param b * byte, the receiver's new flags for the class it represents */ void setFlags(byte b) { flags = b; } /** * Set the name of the class represented by the receiver * * @param newName * a String, the new fully qualified name of the class the * receiver represents */ void setName(String newName) { className = newName; } /** * Set the Serial Version User ID of the class represented by the receiver * * @param l * a long, the new SUID for the class represented by the receiver */ void setSerialVersionUID(long l) { svUID = l; } /** * Set the descriptor for the superclass of the class described by the * receiver * * @param c * an ObjectStreamClass, the new ObjectStreamClass for the * superclass of the class represented by the receiver */ void setSuperclass(ObjectStreamClass c) { superclass = c; } private int primitiveSize(Class<?> type) { if (type == Byte.TYPE || type == Boolean.TYPE) { return 1; } if (type == Short.TYPE || type == Character.TYPE) { return 2; } if (type == Integer.TYPE || type == Float.TYPE) { return 4; } if (type == Long.TYPE || type == Double.TYPE) { return 8; } return 0; } /** * Returns a string containing a concise, human-readable description of this * descriptor. * * @return a printable representation of this descriptor. */ @Override public String toString() { return getName() + ": static final long serialVersionUID =" //$NON-NLS-1$ + getSerialVersionUID() + "L;"; //$NON-NLS-1$ } static class OSCThreadLocalCache extends ThreadLocalCache { // thread-local cache for ObjectStreamClass.lookup public static ThreadLocalCache<WeakHashMap<Class<?>,ObjectStreamClass>> oscWeakHashMap = new ThreadLocalCache<WeakHashMap<Class<?>,ObjectStreamClass>>() { protected WeakHashMap<Class<?>,ObjectStreamClass> initialValue() { return new WeakHashMap<Class<?>,ObjectStreamClass>(); } }; } }