0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package java.io;
0019:
0020: import java.lang.ref.WeakReference;
0021: import java.lang.reflect.Constructor;
0022: import java.lang.reflect.Field;
0023: import java.lang.reflect.Method;
0024: import java.lang.reflect.Modifier;
0025: import java.lang.reflect.Proxy;
0026: import java.security.AccessController;
0027: import java.security.MessageDigest;
0028: import java.security.NoSuchAlgorithmException;
0029: import java.util.ArrayList;
0030: import java.util.Arrays;
0031: import java.util.Comparator;
0032: import java.util.List;
0033: import java.util.WeakHashMap;
0034:
0035: import org.apache.harmony.luni.util.Msg;
0036: import org.apache.harmony.luni.util.PriviAction;
0037:
0038: /**
0039: * Instances of ObjectStreamClass are used to describe classes of objects used
0040: * by serialization. When objects are saved, information about all its
0041: * superclasses is also saved by the use of descriptors, instances of
0042: * ObjectStreamClass.
0043: *
0044: * These descriptors carry information about the class they represent, such as -
0045: * The name of the class - SUID of the class - Field names and types
0046: *
0047: * @see ObjectOutputStream
0048: * @see ObjectInputStream
0049: * @see java.lang.Class
0050: */
0051: public class ObjectStreamClass implements Serializable {
0052:
0053: // No need to compute the SUID for ObjectStreamClass, just use the value
0054: // below
0055: private static final long serialVersionUID = -6120832682080437368L;
0056:
0057: // Name of the field that contains the SUID value (if present)
0058: private static final String UID_FIELD_NAME = "serialVersionUID"; //$NON-NLS-1$
0059:
0060: private static final int CLASS_MODIFIERS_MASK;
0061:
0062: private static final int FIELD_MODIFIERS_MASK;
0063:
0064: private static final int METHOD_MODIFIERS_MASK;
0065:
0066: private static final Class<?>[] READ_PARAM_TYPES;
0067:
0068: private static final Class<?>[] WRITE_PARAM_TYPES;
0069:
0070: static final Class<?>[] EMPTY_CONSTRUCTOR_PARAM_TYPES;
0071:
0072: private static final Class<Void> VOID_CLASS;
0073:
0074: static final Class<?>[] UNSHARED_PARAM_TYPES;
0075:
0076: private static native void oneTimeInitialization();
0077:
0078: static {
0079: oneTimeInitialization();
0080:
0081: CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL
0082: | Modifier.INTERFACE | Modifier.ABSTRACT;
0083: FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
0084: | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
0085: | Modifier.VOLATILE | Modifier.TRANSIENT;
0086: METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
0087: | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
0088: | Modifier.SYNCHRONIZED | Modifier.NATIVE
0089: | Modifier.ABSTRACT | Modifier.STRICT;
0090:
0091: READ_PARAM_TYPES = new Class[1];
0092: WRITE_PARAM_TYPES = new Class[1];
0093: READ_PARAM_TYPES[0] = ObjectInputStream.class;
0094: WRITE_PARAM_TYPES[0] = ObjectOutputStream.class;
0095: EMPTY_CONSTRUCTOR_PARAM_TYPES = new Class[0];
0096: VOID_CLASS = Void.TYPE;
0097: UNSHARED_PARAM_TYPES = new Class[1];
0098: UNSHARED_PARAM_TYPES[0] = Object.class;
0099: }
0100:
0101: /**
0102: * A value that indicates the class has no Serializable fields
0103: */
0104: public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0];
0105:
0106: /*
0107: * used to fetch field serialPersistentFields and checking its type
0108: */
0109: static final Class<?> ARRAY_OF_FIELDS;
0110:
0111: static {
0112: try {
0113: ARRAY_OF_FIELDS = Class
0114: .forName("[Ljava.io.ObjectStreamField;"); //$NON-NLS-1$
0115: } catch (ClassNotFoundException e) {
0116: // This should not happen
0117: throw new AssertionError(e);
0118: }
0119: }
0120:
0121: private static final String CLINIT_NAME = "<clinit>"; //$NON-NLS-1$
0122:
0123: private static final int CLINIT_MODIFIERS = Modifier.STATIC;
0124:
0125: private static final String CLINIT_SIGNATURE = "()V"; //$NON-NLS-1$
0126:
0127: // Used to determine if an object is Serializable or Externalizable
0128: private static final Class<Serializable> SERIALIZABLE = Serializable.class;
0129:
0130: private static final Class<Externalizable> EXTERNALIZABLE = Externalizable.class;
0131:
0132: // Used to test if the object is a String or a class.
0133: static final Class<String> STRINGCLASS = String.class;
0134:
0135: static final Class<?> CLASSCLASS = Class.class;
0136:
0137: static final Class<ObjectStreamClass> OBJECTSTREAMCLASSCLASS = ObjectStreamClass.class;
0138:
0139: // Table mapping instances of java.lang.Class to to corresponding instances
0140: // of ObjectStreamClass
0141: private static final WeakHashMap<Class<?>, ObjectStreamClass> classesAndDescriptors = new WeakHashMap<Class<?>, ObjectStreamClass>();
0142:
0143: private transient Method methodWriteReplace;
0144:
0145: private transient Method methodReadResolve;
0146:
0147: private transient Method methodWriteObject;
0148:
0149: private transient Method methodReadObject;
0150:
0151: private transient Method methodReadObjectNoData;
0152:
0153: // ClassDesc //
0154:
0155: // Name of the class this descriptor represents
0156: private transient String className;
0157:
0158: // Corresponding loaded class with the name above
0159: private transient WeakReference<Class<?>> resolvedClass;
0160:
0161: // Serial version UID of the class the descriptor represents
0162: private transient long svUID;
0163:
0164: // ClassDescInfo //
0165:
0166: // Any combination of SC_WRITE_METHOD, SC_SERIALIZABLE and SC_EXTERNALIZABLE
0167: // (see ObjectStreamConstants)
0168: private transient byte flags;
0169:
0170: // Descriptor for the superclass of the class associated with this
0171: // descriptor
0172: private transient ObjectStreamClass super class;
0173:
0174: // Array of ObjectStreamField (see below) describing the fields of this
0175: // class
0176: private transient ObjectStreamField[] fields;
0177:
0178: // Array of ObjectStreamField describing the serialized fields of this class
0179: private transient ObjectStreamField[] loadFields;
0180:
0181: /*
0182: * If an ObjectStreamClass describes an Externalizable class, it (the
0183: * descriptor) should not have field descriptors (ObjectStreamField) at all.
0184: * The ObjectStreamClass that gets saved should simply have no field info.
0185: * This is a footnote in page 1511 (class Serializable) of "The Java Class
0186: * Libraries, Second Edition, Vol. I".
0187: */
0188:
0189: /**
0190: * Constructs a new instance of this class.
0191: */
0192: ObjectStreamClass() {
0193: super ();
0194: }
0195:
0196: /**
0197: * Compute class descriptor for a given class <code>cl</code>. If
0198: * <code>computeSUID</code> is true, this method will compute the SUID for
0199: * this class.
0200: *
0201: * @param cl
0202: * a java.langClass for which to compute the corresponding
0203: * descriptor
0204: * @param computeSUID
0205: * a boolean indicating if SUID should be computed or not.
0206: * @return the computer class descriptor
0207: */
0208: private static ObjectStreamClass createClassDesc(Class<?> cl,
0209: boolean computeSUID) {
0210:
0211: ObjectStreamClass result = new ObjectStreamClass();
0212:
0213: boolean isProxy = Proxy.isProxyClass(cl);
0214: boolean isEnum = Enum.class.isAssignableFrom(cl);
0215: boolean isArray = cl.isArray();
0216:
0217: // Now we fill in the values
0218: result.setName(cl.getName());
0219: result.setClass(cl);
0220: Class<?> super class = cl.getSuperclass();
0221: if (super class != null) {
0222: result.setSuperclass(lookup(super class));
0223: }
0224:
0225: Field[] declaredFields = null;
0226: if (computeSUID) {
0227: // Lazy computation, to save speed & space
0228: if (isEnum || isProxy) {
0229: result.setSerialVersionUID(0L);
0230: } else {
0231: declaredFields = cl.getDeclaredFields();
0232: result.setSerialVersionUID(computeSerialVersionUID(cl,
0233: declaredFields));
0234: }
0235: }
0236:
0237: boolean serializable = isSerializable(cl);
0238: // Serializables need field descriptors
0239: if (serializable && !isArray) {
0240: if (declaredFields == null) {
0241: declaredFields = cl.getDeclaredFields();
0242: }
0243: result.buildFieldDescriptors(declaredFields);
0244: } else {
0245: // Externalizables or arrays do not need FieldDesc info
0246: result.setFields(NO_FIELDS);
0247: }
0248:
0249: // Copy all fields to loadFields - they should be read by default in
0250: // ObjectInputStream.defaultReadObject() method
0251: ObjectStreamField[] fields = result.getFields();
0252:
0253: if (fields != null) {
0254: ObjectStreamField[] loadFields = new ObjectStreamField[fields.length];
0255:
0256: for (int i = 0; i < fields.length; ++i) {
0257: loadFields[i] = new ObjectStreamField(fields[i]
0258: .getName(), fields[i].getType(), fields[i]
0259: .isUnshared());
0260:
0261: // resolve type string to init typeString field in
0262: // ObjectStreamField
0263: loadFields[i].getTypeString();
0264: }
0265: result.setLoadFields(loadFields);
0266: }
0267:
0268: byte flags = 0;
0269: boolean externalizable = isExternalizable(cl);
0270: if (externalizable) {
0271: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
0272: flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default
0273: } else if (serializable) {
0274: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
0275: }
0276: result.methodWriteReplace = findMethod(cl, "writeReplace"); //$NON-NLS-1$
0277: result.methodReadResolve = findMethod(cl, "readResolve"); //$NON-NLS-1$
0278: result.methodWriteObject = findPrivateMethod(cl, "writeObject", //$NON-NLS-1$
0279: WRITE_PARAM_TYPES);
0280: result.methodReadObject = findPrivateMethod(cl, "readObject", //$NON-NLS-1$
0281: READ_PARAM_TYPES);
0282: result.methodReadObjectNoData = findPrivateMethod(cl,
0283: "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES); //$NON-NLS-1$
0284: if (result.hasMethodWriteObject()) {
0285: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
0286: }
0287: result.setFlags(flags);
0288:
0289: return result;
0290: }
0291:
0292: /**
0293: * Builds the collection of field descriptors for the receiver
0294: *
0295: * @param declaredFields
0296: * collection of java.lang.reflect.Field for which to compute
0297: * field descriptors
0298: */
0299: void buildFieldDescriptors(Field[] declaredFields) {
0300: // We could find the field ourselves in the collection, but calling
0301: // reflect is easier. Optimize if needed.
0302: final Field f = ObjectStreamClass
0303: .fieldSerialPersistentFields(this .forClass());
0304: // If we could not find the emulated fields, we'll have to compute
0305: // dumpable fields from reflect fields
0306: boolean useReflectFields = f == null; // Assume we will compute the
0307: // fields to dump based on the
0308: // reflect fields
0309:
0310: ObjectStreamField[] _fields = null;
0311: if (!useReflectFields) {
0312: // The user declared a collection of emulated fields. Use them.
0313: // We have to be able to fetch its value, even if it is private
0314: AccessController.doPrivileged(new PriviAction<Object>(f));
0315: try {
0316: // static field, pass null
0317: _fields = (ObjectStreamField[]) f.get(null);
0318: } catch (IllegalAccessException ex) {
0319: // WARNING - what should we do if we have no access ? This
0320: // should not happen.
0321: throw new RuntimeException(ex);
0322: }
0323: } else {
0324: // Compute collection of dumpable fields based on reflect fields
0325: List<ObjectStreamField> serializableFields = new ArrayList<ObjectStreamField>(
0326: declaredFields.length);
0327: // Filter, we are only interested in fields that are serializable
0328: for (int i = 0; i < declaredFields.length; i++) {
0329: Field declaredField = declaredFields[i];
0330: int modifiers = declaredField.getModifiers();
0331: boolean shouldBeSerialized = !(Modifier
0332: .isStatic(modifiers) || Modifier
0333: .isTransient(modifiers));
0334: if (shouldBeSerialized) {
0335: ObjectStreamField field = new ObjectStreamField(
0336: declaredField.getName(), declaredField
0337: .getType());
0338: serializableFields.add(field);
0339: }
0340: }
0341:
0342: if (serializableFields.size() == 0) {
0343: _fields = NO_FIELDS; // If no serializable fields, share the
0344: // special value so that users can test
0345: } else {
0346: // Now convert from Vector to array
0347: _fields = new ObjectStreamField[serializableFields
0348: .size()];
0349: _fields = serializableFields.toArray(_fields);
0350: }
0351: }
0352: ObjectStreamField.sortFields(_fields);
0353: // assign offsets
0354: int primOffset = 0, objectOffset = 0;
0355: for (int i = 0; i < _fields.length; i++) {
0356: Class<?> type = _fields[i].getType();
0357: if (type.isPrimitive()) {
0358: _fields[i].offset = primOffset;
0359: primOffset += primitiveSize(type);
0360: } else {
0361: _fields[i].offset = objectOffset++;
0362: }
0363: }
0364: fields = _fields;
0365: }
0366:
0367: /**
0368: * Compute and return the Serial Version UID of the class <code>cl</code>.
0369: * The value is computed based on the class name, superclass chain, field
0370: * names, method names, modifiers, etc.
0371: *
0372: * @param cl
0373: * a java.lang.Class for which to compute the SUID
0374: * @param fields
0375: * cl.getDeclaredFields(), pre-computed by the caller
0376: * @return the value of SUID of this class
0377: */
0378: private static long computeSerialVersionUID(Class<?> cl,
0379: Field[] fields) {
0380: /*
0381: * First we should try to fetch the static slot 'static final long
0382: * serialVersionUID'. If it is defined, return it. If not defined, we
0383: * really need to compute SUID using SHAOutputStream
0384: */
0385: for (int i = 0; i < fields.length; i++) {
0386: final Field field = fields[i];
0387: if (Long.TYPE == field.getType()) {
0388: int modifiers = field.getModifiers();
0389: if (Modifier.isStatic(modifiers)
0390: && Modifier.isFinal(modifiers)) {
0391: if (UID_FIELD_NAME.equals(field.getName())) {
0392: /*
0393: * We need to be able to see it even if we have no
0394: * visibility. That is why we set accessible first (new
0395: * API in reflect 1.2)
0396: */
0397: AccessController
0398: .doPrivileged(new PriviAction<Object>(
0399: field));
0400: try {
0401: // Static field, parameter is ignored
0402: return field.getLong(null);
0403: } catch (IllegalAccessException iae) {
0404: throw new RuntimeException(Msg.getString(
0405: "K0071", iae)); //$NON-NLS-1$
0406: }
0407: }
0408: }
0409: }
0410: }
0411:
0412: MessageDigest digest;
0413: try {
0414: digest = MessageDigest.getInstance("SHA"); //$NON-NLS-1$
0415: } catch (NoSuchAlgorithmException e) {
0416: throw new Error(e);
0417: }
0418: ByteArrayOutputStream sha = new ByteArrayOutputStream();
0419: try {
0420: DataOutputStream output = new DataOutputStream(sha);
0421: output.writeUTF(cl.getName());
0422: int classModifiers = CLASS_MODIFIERS_MASK
0423: & cl.getModifiers();
0424: /*
0425: * Workaround for 1F9LOQO. Arrays are ABSTRACT in JDK, but that is
0426: * not in the specification. Since we want to be compatible for
0427: * X-loading, we have to pretend we have the same shape
0428: */
0429: boolean isArray = cl.isArray();
0430: if (isArray) {
0431: classModifiers |= Modifier.ABSTRACT;
0432: }
0433: // Required for JDK UID compatibility
0434: if (cl.isInterface() && !Modifier.isPublic(classModifiers)) {
0435: classModifiers &= ~Modifier.ABSTRACT;
0436: }
0437: output.writeInt(classModifiers);
0438:
0439: /*
0440: * In JDK1.2 arrays implement Cloneable and Serializable but not in
0441: * JDK 1.1.7. So, JDK 1.2 "pretends" arrays have no interfaces when
0442: * computing SHA-1 to be compatible.
0443: */
0444: if (!isArray) {
0445: // Interface information
0446: Class<?>[] interfaces = cl.getInterfaces();
0447: if (interfaces.length > 1) {
0448: // Only attempt to sort if really needed (saves object
0449: // creation, etc)
0450: Comparator<Class<?>> interfaceComparator = new Comparator<Class<?>>() {
0451: public int compare(Class<?> itf1, Class<?> itf2) {
0452: return itf1.getName().compareTo(
0453: itf2.getName());
0454: }
0455: };
0456: Arrays.sort(interfaces, interfaceComparator);
0457: }
0458:
0459: // Dump them
0460: for (int i = 0; i < interfaces.length; i++) {
0461: output.writeUTF(interfaces[i].getName());
0462: }
0463: }
0464:
0465: // Field information
0466: if (fields.length > 1) {
0467: // Only attempt to sort if really needed (saves object creation,
0468: // etc)
0469: Comparator<Field> fieldComparator = new Comparator<Field>() {
0470: public int compare(Field field1, Field field2) {
0471: return field1.getName().compareTo(
0472: field2.getName());
0473: }
0474: };
0475: Arrays.sort(fields, fieldComparator);
0476: }
0477:
0478: // Dump them
0479: for (int i = 0; i < fields.length; i++) {
0480: Field field = fields[i];
0481: int modifiers = field.getModifiers()
0482: & FIELD_MODIFIERS_MASK;
0483:
0484: boolean skip = Modifier.isPrivate(modifiers)
0485: && (Modifier.isTransient(modifiers) || Modifier
0486: .isStatic(modifiers));
0487: if (!skip) {
0488: // write name, modifier & "descriptor" of all but private
0489: // static and private transient
0490: output.writeUTF(field.getName());
0491: output.writeInt(modifiers);
0492: output
0493: .writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
0494: }
0495: }
0496:
0497: /*
0498: * Normally constructors come before methods (because <init> <
0499: * anyMethodName). However, <clinit> is an exception. Besides,
0500: * reflect will not let us get to it.
0501: */
0502: if (hasClinit(cl)) {
0503: // write name, modifier & "descriptor"
0504: output.writeUTF(CLINIT_NAME);
0505: output.writeInt(CLINIT_MODIFIERS);
0506: output.writeUTF(CLINIT_SIGNATURE);
0507: }
0508:
0509: // Constructor information
0510: Constructor<?>[] constructors = cl
0511: .getDeclaredConstructors();
0512: if (constructors.length > 1) {
0513: // Only attempt to sort if really needed (saves object creation,
0514: // etc)
0515: Comparator<Constructor<?>> constructorComparator = new Comparator<Constructor<?>>() {
0516: public int compare(Constructor<?> ctr1,
0517: Constructor<?> ctr2) {
0518: // All constructors have same name, so we sort based on
0519: // signature
0520: return (getConstructorSignature(ctr1)
0521: .compareTo(getConstructorSignature(ctr2)));
0522: }
0523: };
0524: Arrays.sort(constructors, constructorComparator);
0525: }
0526:
0527: // Dump them
0528: for (int i = 0; i < constructors.length; i++) {
0529: Constructor<?> constructor = constructors[i];
0530: int modifiers = constructor.getModifiers()
0531: & METHOD_MODIFIERS_MASK;
0532: boolean isPrivate = Modifier.isPrivate(modifiers);
0533: if (!isPrivate) {
0534: /*
0535: * write name, modifier & "descriptor" of all but private
0536: * ones
0537: *
0538: * constructor.getName() returns the constructor name as
0539: * typed, not the VM name
0540: */
0541: output.writeUTF("<init>"); //$NON-NLS-1$
0542: output.writeInt(modifiers);
0543: output.writeUTF(descriptorForSignature(
0544: getConstructorSignature(constructor))
0545: .replace('/', '.'));
0546: }
0547: }
0548:
0549: // Method information
0550: Method[] methods = cl.getDeclaredMethods();
0551: if (methods.length > 1) {
0552: Comparator<Method> methodComparator = new Comparator<Method>() {
0553: public int compare(Method m1, Method m2) {
0554: int result = m1.getName().compareTo(
0555: m2.getName());
0556: if (result == 0) {
0557: // same name, signature will tell which one comes
0558: // first
0559: return getMethodSignature(m1).compareTo(
0560: getMethodSignature(m2));
0561: }
0562: return result;
0563: }
0564: };
0565: Arrays.sort(methods, methodComparator);
0566: }
0567:
0568: // Dump them
0569: for (int i = 0; i < methods.length; i++) {
0570: Method method = methods[i];
0571: int modifiers = method.getModifiers()
0572: & METHOD_MODIFIERS_MASK;
0573: boolean isPrivate = Modifier.isPrivate(modifiers);
0574: if (!isPrivate) {
0575: // write name, modifier & "descriptor" of all but private
0576: // ones
0577: output.writeUTF(method.getName());
0578: output.writeInt(modifiers);
0579: output.writeUTF(descriptorForSignature(
0580: getMethodSignature(method)).replace('/',
0581: '.'));
0582: }
0583: }
0584: } catch (IOException e) {
0585: throw new RuntimeException(Msg.getString("K0072", e));//$NON-NLS-1$
0586: }
0587:
0588: // now compute the UID based on the SHA
0589: byte[] hash = digest.digest(sha.toByteArray());
0590:
0591: return littleEndianLongAt(hash, 0);
0592: }
0593:
0594: /**
0595: * Return what the serializaton specification calls "descriptor" given a
0596: * field signature. signature.
0597: *
0598: * @param signature
0599: * a field signature
0600: * @return containing the descriptor
0601: */
0602: private static String descriptorForFieldSignature(String signature) {
0603: return signature.replace('.', '/');
0604: }
0605:
0606: /**
0607: * Return what the serializaton specification calls "descriptor" given a
0608: * method/constructor signature.
0609: *
0610: * @param signature
0611: * a method or constructor signature
0612: * @return containing the descriptor
0613: */
0614: private static String descriptorForSignature(String signature) {
0615: return signature.substring(signature.indexOf("(")); //$NON-NLS-1$
0616: }
0617:
0618: /**
0619: * Return the java.lang.reflect.Field <code>serialPersistentFields</code>
0620: * if class <code>cl</code> implements it. Return null otherwise.
0621: *
0622: * @param cl
0623: * a java.lang.Class which to test
0624: * @return <code>java.lang.reflect.Field</code> if the class has
0625: * serialPersistentFields <code>null</code> if the class does not
0626: * have serialPersistentFields
0627: */
0628: static Field fieldSerialPersistentFields(Class<?> cl) {
0629: try {
0630: Field f = cl.getDeclaredField("serialPersistentFields"); //$NON-NLS-1$
0631: int modifiers = f.getModifiers();
0632: if (Modifier.isStatic(modifiers)
0633: && Modifier.isPrivate(modifiers)
0634: && Modifier.isFinal(modifiers)) {
0635: if (f.getType() == ARRAY_OF_FIELDS) {
0636: return f;
0637: }
0638: }
0639: } catch (NoSuchFieldException nsm) {
0640: // Ignored
0641: }
0642: return null;
0643: }
0644:
0645: /**
0646: * Return the class (java.lang.Class) that the receiver represents
0647: *
0648: * @return <code>null</code> if there is no corresponding class for the
0649: * receiver <code>Class</code> The loaded class corresponding to
0650: * the receiver
0651: */
0652: public Class<?> forClass() {
0653: if (resolvedClass != null) {
0654: return resolvedClass.get();
0655: }
0656: return null;
0657: }
0658:
0659: /**
0660: * Return a String representing the signature for a Constructor
0661: * <code>c</code>.
0662: *
0663: * @param c
0664: * a java.lang.reflect.Constructor for which to compute the
0665: * signature
0666: * @return the constructor's signature
0667: *
0668: */
0669: static native String getConstructorSignature(Constructor<?> c);
0670:
0671: /**
0672: * Answers a given field by name.
0673: *
0674: * @param name
0675: * name of the desired field.
0676: * @return a given field by name.
0677: */
0678: public ObjectStreamField getField(String name) {
0679: ObjectStreamField[] allFields = getFields();
0680: for (int i = 0; i < allFields.length; i++) {
0681: ObjectStreamField f = allFields[i];
0682: if (f.getName().equals(name)) {
0683: return f;
0684: }
0685: }
0686: return null;
0687: }
0688:
0689: /**
0690: * Answers the collection of field descriptors for the fields of the
0691: * corresponding class
0692: *
0693: * @return the receiver's collection of declared fields for the class it
0694: * represents
0695: */
0696: ObjectStreamField[] fields() {
0697: if (fields == null) {
0698: Class<?> forCl = forClass();
0699: if (forCl != null && isSerializable(forCl)
0700: && !forCl.isArray()) {
0701: buildFieldDescriptors(forCl.getDeclaredFields());
0702: } else {
0703: // Externalizables or arrays do not need FieldDesc info
0704: setFields(NO_FIELDS);
0705: }
0706: }
0707: return fields;
0708: }
0709:
0710: /**
0711: * Answers the collection of field descriptors for the fields of the
0712: * corresponding class
0713: *
0714: * @return the receiver's collection of declared fields for the class it
0715: * represents
0716: */
0717: public ObjectStreamField[] getFields() {
0718: copyFieldAttributes();
0719: return loadFields == null ? fields().clone() : loadFields
0720: .clone();
0721: }
0722:
0723: /**
0724: * If a Class uses "serialPersistentFields" to define the serialized fields,
0725: * this.loadFields cannot get the "unshared" information when deserializing
0726: * fields using current implementation of ObjectInputStream. This method
0727: * provides a way to copy the "unshared" attribute from this.fields.
0728: *
0729: */
0730: private void copyFieldAttributes() {
0731: if ((loadFields == null) || fields == null) {
0732: return;
0733: }
0734:
0735: for (int i = 0; i < loadFields.length; i++) {
0736: ObjectStreamField loadField = loadFields[i];
0737: String name = loadField.getName();
0738: for (int j = 0; j < fields.length; j++) {
0739: ObjectStreamField field = fields[j];
0740: if (name.equals(field.getName())) {
0741: loadField.setUnshared(field.isUnshared());
0742: loadField.setOffset(field.getOffset());
0743: break;
0744: }
0745: }
0746: }
0747: }
0748:
0749: /**
0750: * Answers the collection of field descriptors for the input fields of the
0751: * corresponding class
0752: *
0753: * @return the receiver's collection of input fields for the class it
0754: * represents
0755: */
0756: ObjectStreamField[] getLoadFields() {
0757: return loadFields;
0758: }
0759:
0760: /**
0761: * Return a String representing the signature for a field <code>f</code>.
0762: *
0763: * @param f
0764: * a java.lang.reflect.Field for which to compute the signature
0765: * @return the field's signature
0766: */
0767: private static native String getFieldSignature(Field f);
0768:
0769: /**
0770: * Answers the flags for this descriptor, where possible combined values are
0771: *
0772: * ObjectStreamConstants.SC_WRITE_METHOD
0773: * ObjectStreamConstants.SC_SERIALIZABLE
0774: * ObjectStreamConstants.SC_EXTERNALIZABLE
0775: *
0776: * @return byte the receiver's flags for the class it represents
0777: */
0778: byte getFlags() {
0779: return flags;
0780: }
0781:
0782: /**
0783: * Return a String representing the signature for a method <code>m</code>.
0784: *
0785: * @param m
0786: * a java.lang.reflect.Method for which to compute the signature
0787: * @return the method's signature
0788: */
0789: static native String getMethodSignature(Method m);
0790:
0791: /**
0792: * Answers the name of the class represented by the receiver
0793: *
0794: * @return fully qualified name of the class the receiver represents
0795: */
0796: public String getName() {
0797: return className;
0798: }
0799:
0800: /**
0801: * Answers the Serial Version User ID of the class represented by the
0802: * receiver
0803: *
0804: * @return SUID for the class represented by the receiver
0805: */
0806: public long getSerialVersionUID() {
0807: return svUID;
0808: }
0809:
0810: /**
0811: * Answers the descriptor (ObjectStreamClass) of the superclass of the class
0812: * represented by the receiver.
0813: *
0814: * @return an ObjectStreamClass representing the superclass of the class
0815: * represented by the receiver.
0816: */
0817: ObjectStreamClass getSuperclass() {
0818: return super class;
0819: }
0820:
0821: /**
0822: * Return true if the given class <code>cl</code> has the
0823: * compiler-generated method <code>clinit</code>. Even though it is
0824: * compiler-generated, it is used by the serialization code to compute SUID.
0825: * This is unfortunate, since it may depend on compiler optimizations in
0826: * some cases.
0827: *
0828: * @param cl
0829: * a java.lang.Class which to test
0830: * @return <code>true</code> if the class has <clinit> <code>false</code>
0831: * if the class does not have <clinit>
0832: */
0833: private static native boolean hasClinit(Class<?> cl);
0834:
0835: /**
0836: * Return true if instances of class <code>cl</code> are Externalizable,
0837: * false otherwise.
0838: *
0839: * @param cl
0840: * a java.lang.Class which to test
0841: * @return <code>true</code> if instances of the class are Externalizable
0842: * <code>false</code> if instances of the class are not
0843: * Externalizable
0844: *
0845: * @see Object#hashCode
0846: */
0847: static boolean isExternalizable(Class<?> cl) {
0848: return EXTERNALIZABLE.isAssignableFrom(cl);
0849: }
0850:
0851: /**
0852: * Return true if the type code
0853: * <code>typecode<code> describes a primitive type
0854: *
0855: * @param typecode a char describing the typecode
0856: * @return <code>true</code> if the typecode represents a primitive type
0857: * <code>false</code> if the typecode represents an Object type (including arrays)
0858: *
0859: * @see Object#hashCode
0860: */
0861: static boolean isPrimitiveType(char typecode) {
0862: return !(typecode == '[' || typecode == 'L');
0863: }
0864:
0865: /**
0866: * Return true if instances of class <code>cl</code> are Serializable,
0867: * false otherwise.
0868: *
0869: * @param cl
0870: * a java.lang.Class which to test
0871: * @return <code>true</code> if instances of the class are Serializable
0872: * <code>false</code> if instances of the class are not
0873: * Serializable
0874: *
0875: * @see Object#hashCode
0876: */
0877: static boolean isSerializable(Class<?> cl) {
0878: return SERIALIZABLE.isAssignableFrom(cl);
0879: }
0880:
0881: /**
0882: * Return a little endian long stored in a given position of the buffer
0883: *
0884: * @param buffer
0885: * a byte array with the byte representation of the number
0886: * @param position
0887: * index where the number starts in the byte array
0888: * @return the number that was stored in little endian format
0889: */
0890: private static long littleEndianLongAt(byte[] buffer, int position) {
0891: long result = 0;
0892: for (int i = position + 7; i >= position; i--) {
0893: result = (result << 8) + (buffer[i] & 0xff);
0894: }
0895: return result;
0896: }
0897:
0898: /**
0899: * Return the descriptor (ObjectStreamClass) corresponding to the class
0900: * <code>cl</code>. If the class is not Serializable or Externalizable,
0901: * null is returned.
0902: *
0903: * @param cl
0904: * a java.langClass for which to obtain the corresponding
0905: * descriptor
0906: * @return <code>null</code> if instances of the class <code>cl</code>
0907: * are not Serializable or Externalizable
0908: * <code>ObjectStreamClass</code> The corresponding descriptor if
0909: * the class <code>cl</code> is Serializable or Externalizable
0910: */
0911: public static ObjectStreamClass lookup(Class<?> cl) {
0912: boolean serializable = isSerializable(cl);
0913: boolean externalizable = isExternalizable(cl);
0914:
0915: // Has to be either Serializable or Externalizable
0916: if (!serializable && !externalizable) {
0917: return null;
0918: }
0919:
0920: return lookupStreamClass(cl, true);
0921: }
0922:
0923: /**
0924: * Return the descriptor (ObjectStreamClass) corresponding to the class
0925: * <code>cl</code>. Returns an ObjectStreamClass even if instances of the
0926: * class cannot be serialized
0927: *
0928: * @param cl
0929: * a java.langClass for which to obtain the corresponding
0930: * descriptor
0931: * @return the corresponding descriptor
0932: */
0933: static ObjectStreamClass lookupStreamClass(Class<?> cl) {
0934: return lookupStreamClass(cl, isSerializable(cl)
0935: || isExternalizable(cl));
0936: }
0937:
0938: /**
0939: * Return the descriptor (ObjectStreamClass) corresponding to the class
0940: * <code>cl</code>. Returns an ObjectStreamClass even if instances of the
0941: * class cannot be serialized
0942: *
0943: * @param cl
0944: * a <code>java.langClass</code> for which to obtain the
0945: * corresponding descriptor
0946: * @param computeSUID
0947: * a boolean indicating if SUID should be computed or not.
0948: * @return the corresponding descriptor
0949: */
0950: private static ObjectStreamClass lookupStreamClass(Class<?> cl,
0951: boolean computeSUID) {
0952: // Synchronized because of the lookup table 'classesAndDescriptors'
0953:
0954: ObjectStreamClass cachedValue;
0955: synchronized (classesAndDescriptors) {
0956: cachedValue = classesAndDescriptors.get(cl);
0957: if (cachedValue == null) {
0958: cachedValue = createClassDesc(cl, computeSUID);
0959: classesAndDescriptors.put(cl, cachedValue);
0960: }
0961: }
0962: return cachedValue;
0963:
0964: }
0965:
0966: /**
0967: * Return the java.lang.reflect.Method if class <code>cl</code> implements
0968: * <code>methodName</code> . Return null otherwise.
0969: *
0970: * @param cl
0971: * a java.lang.Class which to test
0972: * @return <code>java.lang.reflect.Method</code> if the class implements
0973: * writeReplace <code>null</code> if the class does not implement
0974: * writeReplace
0975: */
0976: static Method findMethod(Class<?> cl, String methodName) {
0977: Class<?> search = cl;
0978: Method method = null;
0979: while (search != null) {
0980: try {
0981: method = search.getDeclaredMethod(methodName,
0982: (Class[]) null);
0983: if (search == cl
0984: || (method.getModifiers() & Modifier.PRIVATE) == 0) {
0985: method.setAccessible(true);
0986: return method;
0987: }
0988: } catch (NoSuchMethodException nsm) {
0989: }
0990: search = search.getSuperclass();
0991: }
0992: return null;
0993: }
0994:
0995: /**
0996: * Return the java.lang.reflect.Method if class <code>cl</code> implements
0997: * private <code>methodName</code> . Return null otherwise.
0998: *
0999: * @param cl
1000: * a java.lang.Class which to test
1001: * @return <code>java.lang.reflect.Method</code> if the class implements
1002: * writeReplace <code>null</code> if the class does not implement
1003: * writeReplace
1004: */
1005: static Method findPrivateMethod(Class<?> cl, String methodName,
1006: Class<?>[] param) {
1007: try {
1008: Method method = cl.getDeclaredMethod(methodName, param);
1009: if (Modifier.isPrivate(method.getModifiers())
1010: && method.getReturnType() == VOID_CLASS) {
1011: method.setAccessible(true);
1012: return method;
1013: }
1014: } catch (NoSuchMethodException nsm) {
1015: // Ignored
1016: }
1017: return null;
1018: }
1019:
1020: boolean hasMethodWriteReplace() {
1021: return (methodWriteReplace != null);
1022: }
1023:
1024: Method getMethodWriteReplace() {
1025: return methodWriteReplace;
1026: }
1027:
1028: boolean hasMethodReadResolve() {
1029: return (methodReadResolve != null);
1030: }
1031:
1032: Method getMethodReadResolve() {
1033: return methodReadResolve;
1034: }
1035:
1036: boolean hasMethodWriteObject() {
1037: return (methodWriteObject != null);
1038: }
1039:
1040: Method getMethodWriteObject() {
1041: return methodWriteObject;
1042: }
1043:
1044: boolean hasMethodReadObject() {
1045: return (methodReadObject != null);
1046: }
1047:
1048: Method getMethodReadObject() {
1049: return methodReadObject;
1050: }
1051:
1052: boolean hasMethodReadObjectNoData() {
1053: return (methodReadObjectNoData != null);
1054: }
1055:
1056: Method getMethodReadObjectNoData() {
1057: return methodReadObjectNoData;
1058: }
1059:
1060: void initPrivateFields(ObjectStreamClass desc) {
1061: methodWriteReplace = desc.methodWriteReplace;
1062: methodReadResolve = desc.methodReadResolve;
1063: methodWriteObject = desc.methodWriteObject;
1064: methodReadObject = desc.methodReadObject;
1065: methodReadObjectNoData = desc.methodReadObjectNoData;
1066: }
1067:
1068: /**
1069: * Set the class (java.lang.Class) that the receiver represents
1070: *
1071: * @param c
1072: * aClass, the new class that the receiver describes
1073: */
1074: void setClass(Class<?> c) {
1075: resolvedClass = new WeakReference<Class<?>>(c);
1076: }
1077:
1078: /**
1079: * Set the collection of field descriptors for the fields of the
1080: * corresponding class
1081: *
1082: * @param f
1083: * ObjectStreamField[], the receiver's new collection of declared
1084: * fields for the class it represents
1085: */
1086: void setFields(ObjectStreamField[] f) {
1087: fields = f;
1088: }
1089:
1090: /**
1091: * Set the collection of field descriptors for the input fields of the
1092: * corresponding class
1093: *
1094: * @param f
1095: * ObjectStreamField[], the receiver's new collection of input
1096: * fields for the class it represents
1097: */
1098: void setLoadFields(ObjectStreamField[] f) {
1099: loadFields = f;
1100: }
1101:
1102: /**
1103: * Set the flags for this descriptor, where possible combined values are
1104: *
1105: * ObjectStreamConstants.SC_WRITE_METHOD
1106: * ObjectStreamConstants.SC_SERIALIZABLE
1107: * ObjectStreamConstants.SC_EXTERNALIZABLE
1108: *
1109: * @param b
1110: * byte, the receiver's new flags for the class it represents
1111: */
1112: void setFlags(byte b) {
1113: flags = b;
1114: }
1115:
1116: /**
1117: * Set the name of the class represented by the receiver
1118: *
1119: * @param newName
1120: * a String, the new fully qualified name of the class the
1121: * receiver represents
1122: */
1123: void setName(String newName) {
1124: className = newName;
1125: }
1126:
1127: /**
1128: * Set the Serial Version User ID of the class represented by the receiver
1129: *
1130: * @param l
1131: * a long, the new SUID for the class represented by the receiver
1132: */
1133: void setSerialVersionUID(long l) {
1134: svUID = l;
1135: }
1136:
1137: /**
1138: * Set the descriptor for the superclass of the class described by the
1139: * receiver
1140: *
1141: * @param c
1142: * an ObjectStreamClass, the new ObjectStreamClass for the
1143: * superclass of the class represented by the receiver
1144: */
1145: void setSuperclass(ObjectStreamClass c) {
1146: super class = c;
1147: }
1148:
1149: private int primitiveSize(Class<?> type) {
1150: if (type == Byte.TYPE || type == Boolean.TYPE) {
1151: return 1;
1152: }
1153: if (type == Short.TYPE || type == Character.TYPE) {
1154: return 2;
1155: }
1156: if (type == Integer.TYPE || type == Float.TYPE) {
1157: return 4;
1158: }
1159: if (type == Long.TYPE || type == Double.TYPE) {
1160: return 8;
1161: }
1162: return 0;
1163: }
1164:
1165: /**
1166: * Answers a string containing a concise, human-readable description of the
1167: * receiver.
1168: *
1169: * @return a printable representation for the receiver.
1170: */
1171: @Override
1172: public String toString() {
1173: return getName() + ": static final long serialVersionUID =" //$NON-NLS-1$
1174: + getSerialVersionUID() + "L;"; //$NON-NLS-1$
1175: }
1176: }
|