*
* In JDK 1.1, all Externalizable instances are not written
* in block-data mode.
* In JDK 1.2, all Externalizable instances, by default, are written
* in block-data mode and the Externalizable instance is terminated with
* tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
* instances.
*
* IMPLEMENTATION NOTE:
* This should have been a mode maintained per stream; however,
* for compatibility reasons, it was only possible to record
* this change per class. All Externalizable classes within
* a given stream should either have this mode enabled or
* disabled. This is enforced by not allowing the PROTOCOL_VERSION
* of a stream to he changed after any objects have been written.
*
* @see ObjectOutputStream#useProtocolVersion
* @see ObjectStreamConstants#PROTOCOL_VERSION_1
* @see ObjectStreamConstants#PROTOCOL_VERSION_2
*
* @since JDK 1.2
*/
boolean hasExternalizableBlockDataMode() {
return hasExternalizableBlockData;
}
/**
* Creates a new instance of the represented class. If the class is
* externalizable, invokes its public no-arg constructor; otherwise, if the
* class is serializable, invokes the no-arg constructor of the first
* non-serializable superclass. Throws UnsupportedOperationException if
* this class descriptor is not associated with a class, if the associated
* class is non-serializable or if the appropriate no-arg constructor is
* inaccessible/unavailable.
*/
Object newInstance()
throws InstantiationException, InvocationTargetException,
UnsupportedOperationException
{
if (cons != null) {
try {
return cons.newInstance();
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
InternalError ie = new InternalError();
ie.initCause( ex ) ;
throw ie ;
}
} else {
throw new UnsupportedOperationException("no constructor for " + ofClass);
}
}
/**
* Returns public no-arg constructor of given class, or null if none found.
* Access checks are disabled on the returned constructor (if any), since
* the defining class may still be non-public.
*/
private static Constructor> getExternalizableConstructor(Class> cl) {
return bridge.newConstructorForExternalization(cl);
}
/**
* Returns subclass-accessible no-arg constructor of first non-serializable
* superclass, or null if none found. Access checks are disabled on the
* returned constructor (if any).
*/
private static Constructor> getSerializableConstructor(Class> cl) {
return bridge.newConstructorForSerialization(cl);
}
/*
* Return the ObjectStreamClass of the local class this one is based on.
*/
final ObjectStreamClass localClassDescriptor() {
return localClassDesc;
}
/*
* Get the Serializability of the class.
*/
boolean isSerializable() {
return serializable;
}
/*
* Get the externalizability of the class.
*/
boolean isExternalizable() {
return externalizable;
}
boolean isNonSerializable() {
return ! (externalizable || serializable);
}
/*
* Calculate the size of the array needed to store primitive data and the
* number of object references to read when reading from the input
* stream.
*/
private void computeFieldInfo() {
primBytes = 0;
objFields = 0;
for (int i = 0; i < fields.length; i++ ) {
switch (fields[i].getTypeCode()) {
case 'B':
case 'Z':
primBytes += 1;
break;
case 'C':
case 'S':
primBytes += 2;
break;
case 'I':
case 'F':
primBytes += 4;
break;
case 'J':
case 'D' :
primBytes += 8;
break;
case 'L':
case '[':
objFields += 1;
break;
}
}
}
private static void msg( String str )
{
System.out.println( str ) ;
}
/* JDK 1.5 has introduced some new modifier bits (such as SYNTHETIC)
* that can affect the SVUID computation (see bug 4897937). These bits
* must be ignored, as otherwise interoperability with ORBs in earlier
* JDK versions can be compromised. I am adding these masks for this
* purpose as discussed in the CCC for this bug (see http://ccc.sfbay/4897937).
*/
public static final int CLASS_MASK = Modifier.PUBLIC | Modifier.FINAL |
Modifier.INTERFACE | Modifier.ABSTRACT ;
public static final int FIELD_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL |
Modifier.TRANSIENT | Modifier.VOLATILE ;
public static final int METHOD_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT |
Modifier.STRICT ;
/*
* 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> cl) {
if (DEBUG_SVUID)
msg( "Computing SerialVersionUID for " + cl ) ;
ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
long h = 0;
try {
MessageDigest md = MessageDigest.getInstance("SHA");
DigestOutputStream mdo = new DigestOutputStream(devnull, md);
DataOutputStream data = new DataOutputStream(mdo);
if (DEBUG_SVUID)
msg( "\twriteUTF( \"" + cl.getName() + "\" )" ) ;
data.writeUTF(cl.getName());
int classaccess = cl.getModifiers();
classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
Modifier.INTERFACE | Modifier.ABSTRACT);
/* Workaround for javac bug that only set ABSTRACT for
* interfaces if the interface had some methods.
* The ABSTRACT bit reflects that the number of methods > 0.
* This is required so correct hashes can be computed
* for existing class files.
* Previously this hack was previously present in the VM.
*/
Method[] method = cl.getDeclaredMethods();
if ((classaccess & Modifier.INTERFACE) != 0) {
classaccess &= (~Modifier.ABSTRACT);
if (method.length > 0) {
classaccess |= Modifier.ABSTRACT;
}
}
// Mask out any post-1.4 attributes
classaccess &= CLASS_MASK ;
if (DEBUG_SVUID)
msg( "\twriteInt( " + classaccess + " ) " ) ;
data.writeInt(classaccess);
/*
* Get the list of interfaces supported,
* Accumulate their names their names in Lexical order
* and add them to the hash
*/
if (!cl.isArray()) {
/* In 1.2fcs, getInterfaces() was modified to return
* {java.lang.Cloneable, java.io.Serializable} when
* called on array classes. These values would upset
* the computation of the hash, so we explicitly omit
* them from its computation.
*/
Class> interfaces[] = cl.getInterfaces();
Arrays.sort(interfaces, compareClassByName);
for (int i = 0; i < interfaces.length; i++) {
if (DEBUG_SVUID)
msg( "\twriteUTF( \"" + interfaces[i].getName() + "\" ) " ) ;
data.writeUTF(interfaces[i].getName());
}
}
/* Sort the field names to get a deterministic order */
Field[] field = cl.getDeclaredFields();
Arrays.sort(field, compareMemberByName);
for (int i = 0; i < field.length; i++) {
Field f = field[i];
/* Include in the hash all fields except those that are
* private transient and private static.
*/
int m = f.getModifiers();
if (Modifier.isPrivate(m) &&
(Modifier.isTransient(m) || Modifier.isStatic(m)))
continue;
if (DEBUG_SVUID)
msg( "\twriteUTF( \"" + f.getName() + "\" ) " ) ;
data.writeUTF(f.getName());
// Mask out any post-1.4 bits
m &= FIELD_MASK ;
if (DEBUG_SVUID)
msg( "\twriteInt( " + m + " ) " ) ;
data.writeInt(m);
if (DEBUG_SVUID)
msg( "\twriteUTF( \"" + getSignature(f.getType()) + "\" ) " ) ;
data.writeUTF(getSignature(f.getType()));
}
if (hasStaticInitializer(cl)) {
if (DEBUG_SVUID)
msg( "\twriteUTF( \"