diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2000-05-19 17:55:34 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2000-05-19 17:55:34 +0000 |
commit | 6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef (patch) | |
tree | 88cf0d32aea197ea8e8198e1206b04c820308615 /libjava/java/io/ObjectStreamClass.java | |
parent | 021c89ed68c151c45021fccf1bb5338ee817314c (diff) | |
download | gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.zip gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.tar.gz gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.tar.bz2 |
Jumbo patch:
* Imported beans and serialization
* Updated IA-64 port
* Miscellaneous bug fixes
From-SVN: r34028
Diffstat (limited to 'libjava/java/io/ObjectStreamClass.java')
-rw-r--r-- | libjava/java/io/ObjectStreamClass.java | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/libjava/java/io/ObjectStreamClass.java b/libjava/java/io/ObjectStreamClass.java new file mode 100644 index 0000000..f799b4f --- /dev/null +++ b/libjava/java/io/ObjectStreamClass.java @@ -0,0 +1,666 @@ +/* ObjectStreamClass.java -- Class used to write class information + about serialized objects. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Vector; +import gnu.java.io.NullOutputStream; +import gnu.java.lang.reflect.TypeSignature; +import gnu.gcj.io.SimpleSHSStream; + + +public class ObjectStreamClass implements Serializable +{ + /** + Returns the <code>ObjectStreamClass</code> for <code>cl</code>. + If <code>cl</code> is null, or is not <code>Serializable</code>, + null is returned. <code>ObjectStreamClass</code>'s are memoized; + later calls to this method with the same class will return the + same <code>ObjectStreamClass</code> object and no recalculation + will be done. + + @see java.io.Serializable + */ + public static ObjectStreamClass lookup (Class cl) + { + if (cl == null) + return null; + + ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (cl); + + if (osc != null) + return osc; + else if (! (Serializable.class).isAssignableFrom (cl)) + return null; + else + { + osc = new ObjectStreamClass (cl); + classLookupTable.put (cl, osc); + return osc; + } + } + + + /** + Returns the name of the class that this + <code>ObjectStreamClass</code> represents. + */ + public String getName () + { + return name; + } + + + /** + Returns the class that this <code>ObjectStreamClass</code> + represents. Null could be returned if this + <code>ObjectStreamClass</code> was read from an + <code>ObjectInputStream</code> and the class it represents cannot + be found or loaded. + + @see java.io.ObjectInputStream + */ + public Class forClass () + { + return clazz; + } + + + /** + Returns the serial version stream-unique identifier for the class + represented by this <code>ObjectStreamClass</code>. This SUID is + either defined by the class as <code>static final long + serialVersionUID</code> or is calculated as specified in + Javasoft's "Object Serialization Specification" XXX: add reference + */ + public long getSerialVersionUID () + { + return uid; + } + + + // Returns the serializable (non-static and non-transient) Fields + // of the class represented by this ObjectStreamClass. The Fields + // are sorted by name. + // XXX doc + public ObjectStreamField[] getFields () + { + ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; + System.arraycopy (fields, 0, copy, 0, fields.length); + return copy; + } + + + // XXX doc + // Can't do binary search since fields is sorted by name and + // primitiveness. + public ObjectStreamField getField (String name) + { + for (int i=0; i < fields.length; i++) + if (fields[i].getName ().equals (name)) + return fields[i]; + return null; + } + + + /** + Returns a textual representation of this + <code>ObjectStreamClass</code> object including the name of the + class it represents as well as that class's serial version + stream-unique identifier. + + @see getSerialVersionUID () + @see getName () + */ + public String toString () + { + return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // has the following method: + // + // private void writeObject (ObjectOutputStream) + // + // This method is used by the class to override default + // serialization behaivior. + boolean hasWriteMethod () + { + return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Serializable but does *not* implement Externalizable. + boolean isSerializable () + { + return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isExternalizable () + { + return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + } + + + // Returns the <code>ObjectStreamClass</code> that represents the + // class that is the superclass of the class this + // <code>ObjectStreamClass</cdoe> represents. If the superclass is + // not Serializable, null is returned. + ObjectStreamClass getSuper () + { + return superClass; + } + + + // returns an array of ObjectStreamClasses that represent the super + // classes of CLAZZ and CLAZZ itself in order from most super to + // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ + // that is serializable. + static ObjectStreamClass[] getObjectStreamClasses (Class clazz) + { + ObjectStreamClass osc = ObjectStreamClass.lookup (clazz); + + ObjectStreamClass[] ret_val; + + if (osc == null) + return new ObjectStreamClass[0]; + else + { + Vector oscs = new Vector (); + + while (osc != null) + { + oscs.addElement (osc); + osc = osc.getSuper (); + } + + int count = oscs.size (); + ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; + + for (int i = count - 1; i >= 0; i--) + sorted_oscs[ count - i - 1 ] = (ObjectStreamClass)oscs.elementAt (i); + + return sorted_oscs; + } + } + + + // Returns an integer that consists of bit-flags that indicate + // properties of the class represented by this ObjectStreamClass. + // The bit-flags that could be present are those defined in + // ObjectStreamConstants that begin with `SC_' + int getFlags () + { + return flags; + } + + + ObjectStreamClass (String name, long uid, byte flags, + ObjectStreamField[] fields) + { + this.name = name; + this.uid = uid; + this.flags = flags; + this.fields = fields; + } + + + void setClass (Class clazz) + { + this.clazz = clazz; + } + + + void setSuperclass (ObjectStreamClass osc) + { + superClass = osc; + } + + + void calculateOffsets () + { + int i; + ObjectStreamField field; + primFieldSize = 0; + int fcount = fields.length; + for (i = 0; i < fcount; ++ i) + { + field = fields[i]; + + if (! field.isPrimitive ()) + break; + + field.setOffset (primFieldSize); + switch (field.getTypeCode ()) + { + case 'B': + case 'Z': + ++ primFieldSize; + break; + case 'C': + case 'S': + primFieldSize += 2; + break; + case 'I': + case 'F': + primFieldSize += 4; + break; + case 'D': + case 'J': + primFieldSize += 8; + break; + } + } + + for (objectFieldCount = 0; i < fcount; ++ i) + fields[i].setOffset (objectFieldCount++); + } + + + private ObjectStreamClass (Class cl) + { + uid = 0; + flags = 0; + + clazz = cl; + name = cl.getName (); + setFlags (cl); + setFields (cl); + setUID (cl); + superClass = lookup (cl.getSuperclass ()); + } + + + // Sets bits in flags according to features of CL. + private void setFlags (Class cl) + { + if ((java.io.Externalizable.class).isAssignableFrom (cl)) + flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + else if ((java.io.Serializable.class).isAssignableFrom (cl)) + // only set this bit if CL is NOT Externalizable + flags |= ObjectStreamConstants.SC_SERIALIZABLE; + + try + { + Method writeMethod = cl.getDeclaredMethod ("writeObject", + writeMethodArgTypes); + int modifiers = writeMethod.getModifiers (); + + if (writeMethod.getReturnType () == Void.TYPE + && Modifier.isPrivate (modifiers) + && !Modifier.isStatic (modifiers)) + flags |= ObjectStreamConstants.SC_WRITE_METHOD; + } + catch (NoSuchMethodException oh_well) + {} + } + + + // Sets fields to be a sorted array of the serializable fields of + // clazz. + private void setFields (Class cl) + { + if (! isSerializable () || isExternalizable ()) + { + fields = NO_FIELDS; + return; + } + + try + { + Field serialPersistantFields + = cl.getDeclaredField ("serialPersistantFields"); + int modifiers = serialPersistantFields.getModifiers (); + + if (Modifier.isStatic (modifiers) + && Modifier.isFinal (modifiers) + && Modifier.isPrivate (modifiers)) + { + fields = getSerialPersistantFields (cl); + Arrays.sort (fields); + calculateOffsets (); + return; + } + } + catch (NoSuchFieldException ignore) + {} + + int num_good_fields = 0; + Field[] all_fields = cl.getDeclaredFields (); + + int modifiers; + // set non-serializable fields to null in all_fields + for (int i=0; i < all_fields.length; i++) + { + modifiers = all_fields[i].getModifiers (); + if (Modifier.isTransient (modifiers) + || Modifier.isStatic (modifiers)) + all_fields[i] = null; + else + num_good_fields++; + } + + // make a copy of serializable (non-null) fields + fields = new ObjectStreamField[ num_good_fields ]; + for (int from=0, to=0; from < all_fields.length; from++) + if (all_fields[from] != null) + { + Field f = all_fields[from]; + fields[to] = new ObjectStreamField (f.getName (), f.getType ()); + to++; + } + + Arrays.sort (fields); + calculateOffsets (); + } + + // Sets uid be serial version UID defined by class, or if that + // isn't present, calculates value of serial version UID. + private void setUID (Class cl) + { + try + { + Field suid = cl.getDeclaredField ("serialVersionUID"); + int modifiers = suid.getModifiers (); + + if (Modifier.isStatic (modifiers) + && Modifier.isFinal (modifiers)) + { + uid = getDefinedSUID (cl); + return; + } + } + catch (NoSuchFieldException ignore) + {} + + // cl didn't define serialVersionUID, so we have to compute it + try + { + MessageDigest md = null; + DigestOutputStream digest_out = null; + DataOutputStream data_out = null; + SimpleSHSStream simple = null; + + try + { + md = MessageDigest.getInstance ("SHA"); + digest_out = new DigestOutputStream (nullOutputStream, md); + data_out = new DataOutputStream (digest_out); + } + catch (NoSuchAlgorithmException e) + { + simple = new SimpleSHSStream (nullOutputStream); + data_out = new DataOutputStream (simple); + } + + data_out.writeUTF (cl.getName ()); + + int modifiers = cl.getModifiers (); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt (modifiers); + + Class[] interfaces = cl.getInterfaces (); + Arrays.sort (interfaces, interfaceComparator); + for (int i=0; i < interfaces.length; i++) + data_out.writeUTF (interfaces[i].getName ()); + + + Field field; + Field[] fields = cl.getDeclaredFields (); + Arrays.sort (fields, memberComparator); + for (int i=0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers (); + if (Modifier.isPrivate (modifiers) + && (Modifier.isStatic (modifiers) + || Modifier.isTransient (modifiers))) + continue; + + data_out.writeUTF (field.getName ()); + data_out.writeInt (modifiers); + data_out.writeUTF (TypeSignature.getEncodingOfClass (field.getType ())); + } + + // write class initializer method if present + boolean has_init; + try + { + has_init = hasClassInitializer (cl); + } + catch (NoSuchMethodError e) + { + has_init = false; + } + + if (has_init) + { + data_out.writeUTF ("<clinit>"); + data_out.writeInt (Modifier.STATIC); + data_out.writeUTF ("()V"); + } + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors (); + Arrays.sort (constructors, memberComparator); + for (int i=0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers (); + if (Modifier.isPrivate (modifiers)) + continue; + + data_out.writeUTF ("<init>"); + data_out.writeInt (modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF ( + TypeSignature.getEncodingOfConstructor (constructor).replace ('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods (); + Arrays.sort (methods, memberComparator); + for (int i=0; i < methods.length; i++) + { + method = methods[i]; + modifiers = method.getModifiers (); + if (Modifier.isPrivate (modifiers)) + continue; + + data_out.writeUTF (method.getName ()); + data_out.writeInt (modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF ( + TypeSignature.getEncodingOfMethod (method).replace ('/', '.')); + } + + data_out.close (); + byte[] sha = md != null ? md.digest () : simple.digest (); + long result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i=0; i < len; i++) + result += (long)(sha[i] & 0xFF) << (8 * i); + + uid = result; + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName ()); + } + catch (IOException ioe) + { + throw new RuntimeException (ioe.getMessage ()); + } + } + + + // Returns the value of CLAZZ's final static long field named + // `serialVersionUID'. + private long getDefinedSUID (Class clazz) + { + long l = 0; + try + { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + Field f = clazz.getDeclaredField ("serialVersionUID"); + l = f.getLong (null); + } + catch (java.lang.NoSuchFieldException e) + { + } + + catch (java.lang.IllegalAccessException e) + { + } + + return l; + } + + // Returns the value of CLAZZ's private static final field named + // `serialPersistantFields'. + private ObjectStreamField[] getSerialPersistantFields (Class clazz) + { + ObjectStreamField[] o = null; + try + { + // Use getDeclaredField rather than getField for the same reason + // as above in getDefinedSUID. + Field f = clazz.getDeclaredField ("getSerialPersistantFields"); + o = (ObjectStreamField[])f.get (null); + } + catch (java.lang.NoSuchFieldException e) + { + } + catch (java.lang.IllegalAccessException e) + { + } + + return o; + } + + + // Returns true if CLAZZ has a static class initializer + // (a.k.a. <clinit>). + // + // A NoSuchMethodError is raised if CLAZZ has no such method. + private static boolean hasClassInitializer (Class clazz) + throws java.lang.NoSuchMethodError + { + Method m = null; + + try + { + Class classArgs[] = {}; + m = clazz.getMethod ("<clinit>", classArgs); + } + catch (java.lang.NoSuchMethodException e) + { + throw new java.lang.NoSuchMethodError (); + } + + return m != null; + } + + public static final ObjectStreamField[] NO_FIELDS = {}; + + private static Hashtable classLookupTable = new Hashtable (); + private static final NullOutputStream nullOutputStream = new NullOutputStream (); + private static final Comparator interfaceComparator = new InterfaceComparator (); + private static final Comparator memberComparator = new MemberComparator (); + private static final + Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; + + private ObjectStreamClass superClass; + private Class clazz; + private String name; + private long uid; + private byte flags; + + // this field is package protected so that ObjectInputStream and + // ObjectOutputStream can access it directly + ObjectStreamField[] fields; + + // these are accessed by ObjectIn/OutputStream + int primFieldSize = -1; // -1 if not yet calculated + int objectFieldCount; +} + + +// interfaces are compared only by name +class InterfaceComparator implements Comparator +{ + public int compare (Object o1, Object o2) + { + return ((Class)o1).getName ().compareTo (((Class)o2).getName ()); + } +} + + +// Members (Methods and Constructors) are compared first by name, +// conflicts are resolved by comparing type signatures +class MemberComparator implements Comparator +{ + public int compare (Object o1, Object o2) + { + Member m1 = (Member)o1; + Member m2 = (Member)o2; + + int comp = m1.getName ().compareTo (m2.getName ()); + + if (comp == 0) + return TypeSignature.getEncodingOfMember (m1). + compareTo (TypeSignature.getEncodingOfMember (m2)); + else + return comp; + } +} |