objectstreamclass.java

来自「《移动Agent技术》一书的所有章节源代码。」· Java 代码 · 共 859 行 · 第 1/2 页

JAVA
859
字号
/*
 * @(#)ObjectStreamClass.java	1.41 98/07/09
 *
 * Copyright 1995-1998 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 * 
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */

package java.io;

import sun.misc.Ref;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.DigestOutputStream;
import java.lang.reflect.Modifier;

/**
 * A ObjectStreamClass describes a class that can be serialized to a stream
 * or a class that was serialized to a stream.  It contains the name
 * and the serialVersionUID of the class.
 * <br>
 * The ObjectStreamClass for a specific class loaded in this Java VM can
 * be found using the lookup method.
 *
 * @author  unascribed
 * @version 1.41, 07/09/98
 * @since   JDK1.1
 */
public class ObjectStreamClass implements java.io.Serializable {

   static final long serialVersionUID = -6120832682080437368L;

   /** Find the descriptor for a class that can be serialized.  Null
     * is returned if the specified class does not implement
     * java.io.Serializable or java.io.Externalizable.
     * @since   JDK1.1
     */
    public static ObjectStreamClass lookup(Class cl)
    {
	/* Synchronize on the hashtable so no two threads will do
	 * this at the same time.
	 */
	ObjectStreamClass v = null;
	synchronized (descriptorFor) {
	    /* Find the matching descriptor if it already known */
	    v = findDescriptorFor(cl);
	    if (v != null) {
		return v;
	    }
	    
	    /* Check if it's serializable or externalizable.
	     * Since Externalizable extends Serializiable, return
	     * null immediately if it's not Serializable.
	     */
	    boolean serializable = classSerializable.isAssignableFrom(cl);
	    if (!serializable)
		return null;

	    /* Test if it's Externalizable, clear the serializable flag
	     * only one or the other may be set in the protocol.
	     */
	    boolean externalizable = classExternalizable.isAssignableFrom(cl);
	    if (externalizable)
		serializable = false;

	    /* If the class is only Serializable,
	     * lookup the descriptor for the superclass.
	     */
	    ObjectStreamClass superdesc = null;
	    if (serializable) {
		Class superclass = cl.getSuperclass();
		if (superclass != null) 
		    superdesc = lookup(superclass);
	    }

	    /* Create a new version descriptor,
	     * it put itself in the known table.
	     */
	    v = new ObjectStreamClass(cl, superdesc,
				      serializable, externalizable);
	}
	return v;
    }
    
    /**
     * The name of the class described by this descriptor.
     * @since   JDK1.1
     */
    public String getName() {
	return name;
    }

    /**
     * Return the serialVersionUID for this class.
     * The serialVersionUID defines a set of classes all with the same name
     * that have evolved from a common root class and agree to be serialized
     * and deserialized using a common format.
     * @since   JDK1.1
     */
    public long getSerialVersionUID() {
	return suid;
    }

    /**
     * Return the class in the local VM that this version is mapped to.
     * Null is returned if there is no corresponding local class.
     * @since   JDK1.1
     */
    public Class forClass() {
	return ofClass;
    }
    
    /**
     * Return a string describing this ObjectStreamClass.
     * @since   JDK1.1
     */
    public String toString() {
	StringBuffer sb = new StringBuffer();

	sb.append(name);
	sb.append(": static final long serialVersionUID = ");
	sb.append(Long.toString(suid));
	sb.append("L;");
	return sb.toString();
    }

    /*
     * Create a new ObjectStreamClass from a loaded class.
     * Don't call this directly, call lookup instead.
     */
    private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc,
			      boolean serial, boolean extern)
    {
	int i;
	ofClass = cl;		/* created from this class */

	name = cl.getName();
	superclass = superdesc;
	serializable = serial;
	externalizable = extern;

	/*
	 * Enter this class in the table of known descriptors.
	 * Otherwise, when the fields are read it may recurse
	 * trying to find the descriptor for itself.
	 */
	insertDescriptorFor(this);

	if (externalizable || name.equals("java.lang.String")) {
	    fields = new ObjectStreamField[0];
	} else {
	    /* Fill in the list of persistent fields. */
	    fields = getFields0(cl);

	    if (fields.length > 0) {
		/* sort the fields by type and name,
		 * primitive fields come first, sorted by name,
		 * then object fields, sorted by name.
		 */
		boolean done;
		do {
		    done = true;
		    for (i = fields.length - 1 ; i > 0 ; i--) {
			if (fields[i - 1].compare(fields[i]) > 0) {
			    ObjectStreamField exch = fields[i];
			    fields[i] = fields[i-1];
			    fields[i-1] = exch;
			    done = false;
			}
		    }
		} while (!done);

		computeFieldSequence();
	    }
	}

	/* Get the serialVersionUID from the class */
	suid = getSerialVersionUID(cl);
	if (suid == 0) {
	    suid = computeSerialVersionUID(cl);
	}
	hasWriteObjectMethod = externalizable ? false : hasWriteObject(cl);
    }

    /*
     * Create an empty ObjectStreamClass for a class about to be read.
     * This is separate from read so ObjectInputStream can assign the
     * wire handle early, before any nested ObjectStreamClasss might
     * be read.
     */
    ObjectStreamClass(String n, long s) {
	name = n;
	suid = s;
	superclass = null;
    }

    /*
     * Set the class this version descriptor matches.
     * The name and serializable hash  must match.
     * Compute and fill in the fieldSequence that will be used
     * for reading.
     */
    void setClass(Class cl) throws InvalidClassException {
	if (cl == null) {

	    /* There is no local equivalent of this class read from the serialized
	     * stream. Initialize this class to always discard data associated with
	     * this class.
	     */
	    localClassDesc = null;
	    ofClass = null;
	    for (int i = 0; i < fields.length; i++ ) {
		fields[i].offset = -1; // discard data read from stream.
	    }
	    computeFieldSequence();
	    return;
	}

	localClassDesc = lookup(cl);

	if (localClassDesc == null)
	    throw new InvalidClassException(cl.getName(), 
					    "Local class not compatible");

	if (suid != localClassDesc.suid) {
	    
	    /* Disregard the serialVersionUID of an array
	     * when name and cl.Name differ. If resolveClass() returns
	     * an array with a different package name,
	     * the serialVersionUIDs will not match since the fully
	     * qualified array class is used in the
	     * computation of the array's serialVersionUID. There is
	     * no way to set a permanent serialVersionUID for an array type.
	     */
	    if (! (cl.isArray() && ! cl.getName().equals(name)))
		throw new InvalidClassException(cl.getName(), 
		    "Local class not compatible:" + 
		    " stream classdesc serialVersionUID=" + suid +
		    " local class serialVersionUID=" + localClassDesc.suid);
	}

	if (! compareClassNames(name, cl.getName(), '.'))
	    throw new InvalidClassException(name,
					    "Incompatible local class name: " +
					    cl.getName());

	/*
	 * Test that both implement either serializable or externalizable.
	 */
	if (serializable != localClassDesc.serializable ||
	    externalizable != localClassDesc.externalizable)
	    throw new InvalidClassException(cl.getName(),
					"Serialization incompatible with Externalization");

	/* Compute the offsets in the class where each field in this descriptor
	 * should be stored.  The fieldSequence is computed from the offsets
	 * and used by the native code to read and store the values.
	 * Each field in this ObjectStreamClass (the source) is located (by name) in
	 * the ObjectStreamClass of the class(the destination).
	 * In the usual (non-versioned case) the field is in both
	 * descriptors and the types match, so the offset is copied.
	 * If the type does not match, a InvalidClass exception is thrown.
	 * If the field is not present in the class, the offset is set to -1
	 * so the field will be read but discarded.
	 * If extra fields are present in the class they are ignored. Their
	 * values will be set to the default value by the object allocator.
	 * Both the src and dest field list are sorted by type and name.
	 */

	ObjectStreamField[] destfield = localClassDesc.getFields();
	ObjectStreamField[] srcfield = fields;

	int j = 0;
    nextsrc:
	for (int i = 0; i < srcfield.length; i++ ) {
	    /* Find this field in the dest*/
	    for (int k = j; k < destfield.length; k++) {
	      if (srcfield[i].name.equals(destfield[k].name)) {
		  /* found match */
		  if (!srcfield[i].typeEquals(destfield[k])) {
		      throw new InvalidClassException(cl.getName(),
						  "The type of field " +
						       srcfield[i].name +
						       " of class " + name +
						       " is incompatible.");
		  }

		  /* Skip over any fields in the dest that are not in the src */
 		  j = k; 
		  
		  srcfield[i].offset = destfield[j].offset;
		  // go on to the next source field
		  continue nextsrc;
	      }
	    }
	    /* Source field not found in dest, mark field to discard. */
	    srcfield[i].offset = -1;
	}

	/* Setup the field sequence for native code */
	computeFieldSequence();

	/* Remember the class this represents */
	ofClass = cl;
    }

    /* Compare the base class names of streamName and localName.
     * 
     * @return  Return true iff the base class name compare.
     * @parameter streamName	Fully qualified class name.
     * @parameter localName	Fully qualified class name.
     * @parameter pkgSeparator	class names use either '.' or '/'.
     * 
     * Only compare base class name to allow package renaming.
     */
    static boolean compareClassNames(String streamName,
					 String localName,
					 char pkgSeparator) {
	/* compare the class names, stripping off package names. */
	int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
	if (streamNameIndex < 0) 
	    streamNameIndex = 0;

	int localNameIndex = localName.lastIndexOf(pkgSeparator);
	if (localNameIndex < 0)
	    localNameIndex = 0;

	boolean result = streamName.regionMatches(false, streamNameIndex, 
					localName, localNameIndex,
					streamName.length() - streamNameIndex);
	return result;
    }

    /*
     * Compare the types of two class descriptors.
     * They match if they have the same class name and suid
     */
    boolean typeEquals(ObjectStreamClass other) {
	return (suid == other.suid) &&
	    compareClassNames(name, other.name, '.');
    }
    
    /*
     * Return the array of persistent fields for this class.
     */
    ObjectStreamField[] getFields(){
	return fields;
    }
    
    /*
     * Return the superclass descriptor of this descriptor.
     */
    void setSuperclass(ObjectStreamClass s) {
	superclass = s;
    }

    /*
     * Return the superclass descriptor of this descriptor.
     */
    ObjectStreamClass getSuperclass() {
	return superclass;
    }
    
    /*
     * Return whether the class has a writeObject method
     */
    boolean hasWriteObject() {
	return hasWriteObjectMethod;
    }
    
    /*
     * Return true if 'this' Externalizable class was written in block data mode.
     * Maintain forwards compatibility for JDK 1.1 streams containing non-block data
     * mode externalizable data.
     *
     * @since JDK 1.1.6
     */
    boolean hasExternalizableBlockDataMode() {
	return hasExternalizableBlockData;
    }

    /*
     * Return the ObjectStreamClass of the local class this one is based on.
     */
    ObjectStreamClass localClassDescriptor() {
	return localClassDesc;
    }
    
    /*
     * Get the externalizability of the class.
     */
    boolean isExternalizable() {
	return externalizable;
    }

    /*
     * Get the sequence of fields for this Class.
     */
    int[] getFieldSequence() {
	return fieldSequence;
    }

    /*
     * Create the array used by the native code containing
     * the types and offsets to store value read from the stream.
     * The array is an array of int's with the even numbered elements
     * containing the type (first character) and the odd elements
     * containing the offset into the object where the value should be
     * stored.  An offset of -1 means the value should be discarded.
     */
    private void computeFieldSequence() {
	fieldSequence = new int[fields.length*2];
	for (int i = 0; i < fields.length; i++ ) {
	    fieldSequence[i*2] = fields[i].type;
	    fieldSequence[i*2+1] = fields[i].offset;
	}
    }
    
    /*
     * Compute a hash for the specified class.  Incrementally add
     * items to the hash accumulating in the digest stream.
     * Fold the hash into a long.  Use the SHA secure hash function.
     */
    private static long computeSerialVersionUID(Class thisclass) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?