package org.unsynchronized; import java.io.*; import java.util.*; /** * <p> * Represents the entire serialized prototype of the class, including all fields, * inner classes, class annotations, and inheritance hierarchy. This includes proxy class * descriptions. * </p> * * <p> * Generally, this class is used to represent the type of an instance written to an * ObjectOutputStream with its writeObject() method, or of a related array or field type. * However, there's a notable exception: when instances of type java.io.ObjectStreamClass * are written with writeObject(), only their class description is written (cf. Object * Serialization Specification, 4.3). They will be represented with an instance of * classdesc as well. * </p> */ public class ClassDesc extends ContentBase { /** * Type of the class being represented; either a normal class or a proxy class. */ public ClassDescType classtype; /** * Class name. */ public String name; /** * Serial version UID, as recorded in the stream. */ public long serialVersionUID; /** * Description flags byte; this should be a mask of values from the ObjectStreamContants * class. Refer to chapter 6 of the Object Stream Serialization Protocol for details. */ public byte descflags; /** * Array of fields in the class, in the order serialized by the stream writer. */ public Field[] fields; /** * List of inner classes, in the order serialized by the stream writer. */ public List<ClassDesc> innerclasses; /** * List of annotation objects; these are *not* Java annotations, but data written by * the <pre>annotateClass(Class<?>)<pre> and <pre>annotateProxyClass(Class<?>)</pre> methods of an * ObjectOutputStream. */ public List<Content> annotations; /** * The superclass of the object, if available. */ public ClassDesc superclass; /** * Array of serialized interfaces, in the order serialized by the stream writer. */ public String[] interfaces; /** * Set of enum constants, for enum classes. */ public Set<String> enumconstants; private boolean isInnerClass = false; /** * True if this class has been determined to be an inner class; this determination is * generally made by connectMemberClasses(). * * @return true if the class is an inner class */ public boolean isInnerClass() { return isInnerClass; } /** * Sets the value that denotes that the class is an inner class. * * @param nis the value to set */ public void setIsInnerClass(boolean nis) { this.isInnerClass = nis; } private boolean isLocalInnerClass = false; /** * True if this class has been determined to be a local inner class; this * determination is generally made by connectMemberClasses(). * * @return true if the class is a local inner class */ public boolean isLocalInnerClass() { return isLocalInnerClass; } /** * Sets the flag that denotes whether this class is a local inner class. * * @param nis the value to set */ public void setIsLocalInnerClass(boolean nis) { this.isLocalInnerClass = nis; } private boolean isStaticMemberClass = false; /** * <p> * True if this class has been determined to be a static member class; this * determination is generally made by connectMemberClasses(). * </p> * * <p> * Note that in some cases, static member classes' descriptions will be serialized * even though their enclosing class is not. In these cases, this may return false. * See connectMemberClasses() for details. * </p> * * @return true if this is a static member class */ public boolean isStaticMemberClass() { return isStaticMemberClass; } /** * Sets the flag that denotes whether this class is a static member class. * * @param nis the value to set */ public void setIsStaticMemberClass(boolean nis) { this.isStaticMemberClass = nis; } /** * Constructor. * * @param classtype the type of the class */ public ClassDesc(ClassDescType classtype) { super(ContentType.CLASSDESC); this.classtype = classtype; this.enumconstants = new HashSet<String>(); this.innerclasses = new ArrayList<ClassDesc>(); } /** * Add an inner class to the description's list. * @param cd inner class to add */ public void addInnerClass(ClassDesc cd) { innerclasses.add(cd); } /** * Add an enum constant to the description's set. * * @param constval enum constant string */ public void addEnum(String constval) { this.enumconstants.add(constval); } /** * Determines whether this is an array type. * @return true if this is an array type. */ public boolean isArrayClass() { if(name != null && name.length() > 1 && name.charAt(0) == '[') { return true; } else { return false; } } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("[cd ").append(JDeserialize.hex(handle)).append(": name ").append(name); sb.append(" uid ").append(serialVersionUID); sb.append("]"); return sb.toString(); } /** * Generates a list of all class descriptions in this class's hierarchy, in the order * described by the Object Stream Serialization Protocol. This is the order in which * fields are read from the stream. * * @param classes a list to be filled in with the hierarchy */ public void getHierarchy(ArrayList<ClassDesc> classes) { if(superclass != null) { if(superclass.classtype == ClassDescType.PROXYCLASS) { JDeserialize.debugerr("warning: hit a proxy class in superclass hierarchy"); } else { superclass.getHierarchy(classes); } } classes.add(this); } public void validate() throws ValidityException { // If neither SC_SERIALIZABLE nor SC_EXTERNALIZABLE is set, then the number of // fields is always zero. (spec section 4.3) if((descflags & (ObjectStreamConstants.SC_SERIALIZABLE | ObjectStreamConstants.SC_EXTERNALIZABLE)) == 0 && fields != null && fields.length > 0) { throw new ValidityException("non-serializable, non-externalizable class has fields!"); } if((descflags & (ObjectStreamConstants.SC_SERIALIZABLE | ObjectStreamConstants.SC_EXTERNALIZABLE)) == (ObjectStreamConstants.SC_SERIALIZABLE | ObjectStreamConstants.SC_EXTERNALIZABLE)) { throw new ValidityException("both Serializable and Externalizable are set!"); } if((descflags & ObjectStreamConstants.SC_ENUM) != 0) { // we're an enum; shouldn't have any fields/superinterfaces if((fields != null && fields.length > 0) || interfaces != null) { throw new ValidityException("enums shouldn't implement interfaces or have non-constant fields!"); } } else { // non-enums shouldn't have enum constant fields. if(enumconstants != null && enumconstants.size() > 0) { throw new ValidityException("non-enum classes shouldn't have enum constants!"); } } } }