1 /* ObjectStreamClass.java -- Class used to write class information
2 about serialized objects.
3 Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
42 import java
.lang
.reflect
.Constructor
;
43 import java
.lang
.reflect
.Field
;
44 import java
.lang
.reflect
.Member
;
45 import java
.lang
.reflect
.Method
;
46 import java
.lang
.reflect
.Modifier
;
47 import java
.lang
.reflect
.Proxy
;
48 import java
.security
.DigestOutputStream
;
49 import java
.security
.MessageDigest
;
50 import java
.security
.NoSuchAlgorithmException
;
51 import java
.security
.Security
;
52 import java
.util
.Arrays
;
53 import java
.util
.Comparator
;
54 import java
.util
.Hashtable
;
55 import java
.util
.Vector
;
56 import gnu
.java
.io
.NullOutputStream
;
57 import gnu
.java
.lang
.reflect
.TypeSignature
;
58 import gnu
.java
.security
.provider
.Gnu
;
61 public class ObjectStreamClass
implements Serializable
64 Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
65 If <code>cl</code> is null, or is not <code>Serializable</code>,
66 null is returned. <code>ObjectStreamClass</code>'s are memorized;
67 later calls to this method with the same class will return the
68 same <code>ObjectStreamClass</code> object and no recalculation
71 @see java.io.Serializable
73 public static ObjectStreamClass
lookup (Class cl
)
77 if (! (Serializable
.class).isAssignableFrom (cl
))
80 return lookupForClassObject (cl
);
84 * This lookup for internal use by ObjectOutputStream. Suppose
85 * we have a java.lang.Class object C for class A, though A is not
86 * serializable, but it's okay to serialize C.
88 static ObjectStreamClass
lookupForClassObject (Class cl
)
93 ObjectStreamClass osc
= (ObjectStreamClass
)classLookupTable
.get (cl
);
99 osc
= new ObjectStreamClass (cl
);
100 classLookupTable
.put (cl
, osc
);
107 Returns the name of the class that this
108 <code>ObjectStreamClass</code> represents.
110 public String
getName ()
117 Returns the class that this <code>ObjectStreamClass</code>
118 represents. Null could be returned if this
119 <code>ObjectStreamClass</code> was read from an
120 <code>ObjectInputStream</code> and the class it represents cannot
123 @see java.io.ObjectInputStream
125 public Class
forClass ()
132 Returns the serial version stream-unique identifier for the class
133 represented by this <code>ObjectStreamClass</code>. This SUID is
134 either defined by the class as <code>static final long
135 serialVersionUID</code> or is calculated as specified in
136 Javasoft's "Object Serialization Specification" XXX: add reference
138 public long getSerialVersionUID ()
144 // Returns the serializable (non-static and non-transient) Fields
145 // of the class represented by this ObjectStreamClass. The Fields
146 // are sorted by name.
148 public ObjectStreamField
[] getFields ()
150 ObjectStreamField
[] copy
= new ObjectStreamField
[ fields
.length
];
151 System
.arraycopy (fields
, 0, copy
, 0, fields
.length
);
157 // Can't do binary search since fields is sorted by name and
159 public ObjectStreamField
getField (String name
)
161 for (int i
=0; i
< fields
.length
; i
++)
162 if (fields
[i
].getName ().equals (name
))
169 * Returns a textual representation of this
170 * <code>ObjectStreamClass</code> object including the name of the
171 * class it represents as well as that class's serial version
172 * stream-unique identifier.
174 * @see #getSerialVersionUID()
177 public String
toString ()
179 return "java.io.ObjectStreamClass< " + name
+ ", " + uid
+ " >";
183 // Returns true iff the class that this ObjectStreamClass represents
184 // has the following method:
186 // private void writeObject (ObjectOutputStream)
188 // This method is used by the class to override default
189 // serialization behavior.
190 boolean hasWriteMethod ()
192 return (flags
& ObjectStreamConstants
.SC_WRITE_METHOD
) != 0;
196 // Returns true iff the class that this ObjectStreamClass represents
197 // has the following method:
199 // private void readObject (ObjectOutputStream)
201 // This method is used by the class to override default
202 // serialization behavior.
203 boolean hasReadMethod ()
207 Class
[] readObjectParams
= { ObjectInputStream
.class };
208 forClass ().getDeclaredMethod ("readObject", readObjectParams
);
211 catch (NoSuchMethodException e
)
218 // Returns true iff the class that this ObjectStreamClass represents
219 // implements Serializable but does *not* implement Externalizable.
220 boolean isSerializable ()
222 return (flags
& ObjectStreamConstants
.SC_SERIALIZABLE
) != 0;
226 // Returns true iff the class that this ObjectStreamClass represents
227 // implements Externalizable.
228 boolean isExternalizable ()
230 return (flags
& ObjectStreamConstants
.SC_EXTERNALIZABLE
) != 0;
234 // Returns the <code>ObjectStreamClass</code> that represents the
235 // class that is the superclass of the class this
236 // <code>ObjectStreamClass</code> represents. If the superclass is
237 // not Serializable, null is returned.
238 ObjectStreamClass
getSuper ()
244 // returns an array of ObjectStreamClasses that represent the super
245 // classes of CLAZZ and CLAZZ itself in order from most super to
246 // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ
247 // that is serializable.
248 static ObjectStreamClass
[] getObjectStreamClasses (Class clazz
)
250 ObjectStreamClass osc
= ObjectStreamClass
.lookup (clazz
);
253 return new ObjectStreamClass
[0];
256 Vector oscs
= new Vector ();
260 oscs
.addElement (osc
);
261 osc
= osc
.getSuper ();
264 int count
= oscs
.size ();
265 ObjectStreamClass
[] sorted_oscs
= new ObjectStreamClass
[ count
];
267 for (int i
= count
- 1; i
>= 0; i
--)
268 sorted_oscs
[ count
- i
- 1 ] = (ObjectStreamClass
)oscs
.elementAt (i
);
275 // Returns an integer that consists of bit-flags that indicate
276 // properties of the class represented by this ObjectStreamClass.
277 // The bit-flags that could be present are those defined in
278 // ObjectStreamConstants that begin with `SC_'
285 ObjectStreamClass (String name
, long uid
, byte flags
,
286 ObjectStreamField
[] fields
)
291 this.fields
= fields
;
295 * This method builds the internal description corresponding to a Java Class.
296 * As the constructor only assign a name to the current ObjectStreamClass instance,
297 * that method sets the serial UID, chose the fields which will be serialized,
298 * and compute the position of the fields in the serialized stream.
300 * @param cl The Java class which is used as a reference for building the descriptor.
301 * @param superClass The descriptor of the super class for this class descriptor.
302 * @throws InvalidClassException if an incompatibility between computed UID and
303 * already set UID is found.
305 void setClass (Class cl
, ObjectStreamClass superClass
) throws InvalidClassException
309 long class_uid
= getClassUID (cl
);
314 // Check that the actual UID of the resolved class matches the UID from
316 if (uid
!= class_uid
)
319 ": Local class not compatible: stream serialVersionUID="
320 + uid
+ ", local serialVersionUID=" + class_uid
;
321 throw new InvalidClassException (msg
);
325 isProxyClass
= clazz
!= null && Proxy
.isProxyClass (clazz
);
326 this.superClass
= superClass
;
331 ObjectStreamField
[] exportedFields
= getSerialPersistentFields (clazz
);
333 if (exportedFields
== null)
336 ObjectStreamField
[] newFieldList
= new ObjectStreamField
[exportedFields
.length
+ fields
.length
];
339 /* We now check the import fields against the exported fields.
340 * There should not be contradiction (e.g. int x and String x)
341 * but extra virtual fields can be added to the class.
344 Arrays
.sort(exportedFields
);
347 while (i
< fields
.length
&& j
< exportedFields
.length
)
349 int comp
= fields
[i
].getName().compareTo (exportedFields
[j
].getName());
352 newFieldList
[k
] = fields
[i
];
353 fields
[i
].setPersistent(false);
354 fields
[i
].setToSet(false);
359 /* field not found in imported fields. We add it
360 * in the list of supported fields.
362 newFieldList
[k
] = exportedFields
[j
];
363 newFieldList
[k
].setPersistent(true);
364 newFieldList
[k
].setToSet(false);
369 if (!fields
[i
].getType().equals (exportedFields
[j
].getType()))
370 throw new InvalidClassException ("serialPersistentFields must be compatible with" +
371 " imported fields (about " + fields
[i
].getName() + ")");
372 newFieldList
[k
] = fields
[i
];
373 fields
[i
].setPersistent(true);
380 if (i
< fields
.length
)
381 for (; i
< fields
.length
; i
++, k
++)
383 fields
[i
].setPersistent(false);
384 fields
[i
].setToSet(false);
385 newFieldList
[k
] = fields
[i
];
388 if (j
< exportedFields
.length
)
389 for (; j
< exportedFields
.length
; j
++, k
++)
391 exportedFields
[j
].setPersistent(true);
392 exportedFields
[j
].setToSet(false);
393 newFieldList
[k
] = exportedFields
[j
];
396 fields
= new ObjectStreamField
[k
];
397 System
.arraycopy (newFieldList
, 0, fields
, 0, k
);
399 catch (NoSuchFieldException ignore
)
403 catch (IllegalAccessException ignore
)
409 void setSuperclass (ObjectStreamClass osc
)
415 void calculateOffsets ()
418 ObjectStreamField field
;
420 int fcount
= fields
.length
;
421 for (i
= 0; i
< fcount
; ++ i
)
425 if (! field
.isPrimitive ())
428 field
.setOffset (primFieldSize
);
429 switch (field
.getTypeCode ())
450 for (objectFieldCount
= 0; i
< fcount
; ++ i
)
451 fields
[i
].setOffset (objectFieldCount
++);
455 private ObjectStreamClass (Class cl
)
459 isProxyClass
= Proxy
.isProxyClass (cl
);
462 name
= cl
.getName ();
465 // to those class nonserializable, its uid field is 0
466 if ( (Serializable
.class).isAssignableFrom (cl
) && !isProxyClass
)
467 uid
= getClassUID (cl
);
468 superClass
= lookup (cl
.getSuperclass ());
472 // Sets bits in flags according to features of CL.
473 private void setFlags (Class cl
)
475 if ((java
.io
.Externalizable
.class).isAssignableFrom (cl
))
476 flags
|= ObjectStreamConstants
.SC_EXTERNALIZABLE
;
477 else if ((java
.io
.Serializable
.class).isAssignableFrom (cl
))
478 // only set this bit if CL is NOT Externalizable
479 flags
|= ObjectStreamConstants
.SC_SERIALIZABLE
;
483 Method writeMethod
= cl
.getDeclaredMethod ("writeObject",
484 writeMethodArgTypes
);
485 int modifiers
= writeMethod
.getModifiers ();
487 if (writeMethod
.getReturnType () == Void
.TYPE
488 && Modifier
.isPrivate (modifiers
)
489 && !Modifier
.isStatic (modifiers
))
490 flags
|= ObjectStreamConstants
.SC_WRITE_METHOD
;
492 catch (NoSuchMethodException oh_well
)
497 // Sets fields to be a sorted array of the serializable fields of
499 private void setFields (Class cl
)
501 if (! isSerializable () || isExternalizable ())
509 Field serialPersistentFields
510 = cl
.getDeclaredField ("serialPersistentFields");
511 serialPersistentFields
.setAccessible(true);
512 int modifiers
= serialPersistentFields
.getModifiers ();
514 if (Modifier
.isStatic (modifiers
)
515 && Modifier
.isFinal (modifiers
)
516 && Modifier
.isPrivate (modifiers
))
518 fields
= getSerialPersistentFields (cl
);
527 catch (NoSuchFieldException ignore
)
529 catch (IllegalAccessException ignore
)
533 int num_good_fields
= 0;
534 Field
[] all_fields
= cl
.getDeclaredFields ();
537 // set non-serializable fields to null in all_fields
538 for (int i
=0; i
< all_fields
.length
; i
++)
540 modifiers
= all_fields
[i
].getModifiers ();
541 if (Modifier
.isTransient (modifiers
)
542 || Modifier
.isStatic (modifiers
))
543 all_fields
[i
] = null;
548 // make a copy of serializable (non-null) fields
549 fields
= new ObjectStreamField
[ num_good_fields
];
550 for (int from
=0, to
=0; from
< all_fields
.length
; from
++)
551 if (all_fields
[from
] != null)
553 Field f
= all_fields
[from
];
554 fields
[to
] = new ObjectStreamField (f
.getName (), f
.getType ());
558 Arrays
.sort (fields
);
562 // Returns the serial version UID defined by class, or if that
563 // isn't present, calculates value of serial version UID.
564 private long getClassUID (Class cl
)
568 // Use getDeclaredField rather than getField, since serialVersionUID
569 // may not be public AND we only want the serialVersionUID of this
570 // class, not a superclass or interface.
571 Field suid
= cl
.getDeclaredField ("serialVersionUID");
572 suid
.setAccessible(true);
573 int modifiers
= suid
.getModifiers ();
575 if (Modifier
.isStatic (modifiers
)
576 && Modifier
.isFinal (modifiers
)
577 && suid
.getType() == Long
.TYPE
)
578 return suid
.getLong (null);
580 catch (NoSuchFieldException ignore
)
582 catch (IllegalAccessException ignore
)
585 // cl didn't define serialVersionUID, so we have to compute it
591 md
= MessageDigest
.getInstance ("SHA");
593 catch (NoSuchAlgorithmException e
)
595 // If a provider already provides SHA, use it; otherwise, use this.
596 Gnu gnuProvider
= new Gnu();
597 Security
.addProvider(gnuProvider
);
598 md
= MessageDigest
.getInstance ("SHA");
601 DigestOutputStream digest_out
=
602 new DigestOutputStream (nullOutputStream
, md
);
603 DataOutputStream data_out
= new DataOutputStream (digest_out
);
605 data_out
.writeUTF (cl
.getName ());
607 int modifiers
= cl
.getModifiers ();
608 // just look at interesting bits
609 modifiers
= modifiers
& (Modifier
.ABSTRACT
| Modifier
.FINAL
610 | Modifier
.INTERFACE
| Modifier
.PUBLIC
);
611 data_out
.writeInt (modifiers
);
613 // Pretend that an array has no interfaces, because when array
614 // serialization was defined (JDK 1.1), arrays didn't have it.
617 Class
[] interfaces
= cl
.getInterfaces ();
618 Arrays
.sort (interfaces
, interfaceComparator
);
619 for (int i
=0; i
< interfaces
.length
; i
++)
620 data_out
.writeUTF (interfaces
[i
].getName ());
624 Field
[] fields
= cl
.getDeclaredFields ();
625 Arrays
.sort (fields
, memberComparator
);
626 for (int i
=0; i
< fields
.length
; i
++)
629 modifiers
= field
.getModifiers ();
630 if (Modifier
.isPrivate (modifiers
)
631 && (Modifier
.isStatic (modifiers
)
632 || Modifier
.isTransient (modifiers
)))
635 data_out
.writeUTF (field
.getName ());
636 data_out
.writeInt (modifiers
);
637 data_out
.writeUTF (TypeSignature
.getEncodingOfClass (field
.getType ()));
640 // write class initializer method if present
641 if (VMObjectStreamClass
.hasClassInitializer (cl
))
643 data_out
.writeUTF ("<clinit>");
644 data_out
.writeInt (Modifier
.STATIC
);
645 data_out
.writeUTF ("()V");
648 Constructor constructor
;
649 Constructor
[] constructors
= cl
.getDeclaredConstructors ();
650 Arrays
.sort (constructors
, memberComparator
);
651 for (int i
=0; i
< constructors
.length
; i
++)
653 constructor
= constructors
[i
];
654 modifiers
= constructor
.getModifiers ();
655 if (Modifier
.isPrivate (modifiers
))
658 data_out
.writeUTF ("<init>");
659 data_out
.writeInt (modifiers
);
661 // the replacement of '/' with '.' was needed to make computed
662 // SUID's agree with those computed by JDK
664 TypeSignature
.getEncodingOfConstructor (constructor
).replace ('/','.'));
668 Method
[] methods
= cl
.getDeclaredMethods ();
669 Arrays
.sort (methods
, memberComparator
);
670 for (int i
=0; i
< methods
.length
; i
++)
673 modifiers
= method
.getModifiers ();
674 if (Modifier
.isPrivate (modifiers
))
677 data_out
.writeUTF (method
.getName ());
678 data_out
.writeInt (modifiers
);
680 // the replacement of '/' with '.' was needed to make computed
681 // SUID's agree with those computed by JDK
683 TypeSignature
.getEncodingOfMethod (method
).replace ('/', '.'));
687 byte[] sha
= md
.digest ();
689 int len
= sha
.length
< 8 ? sha
.length
: 8;
690 for (int i
=0; i
< len
; i
++)
691 result
+= (long)(sha
[i
] & 0xFF) << (8 * i
);
695 catch (NoSuchAlgorithmException e
)
697 throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
700 catch (IOException ioe
)
702 throw new RuntimeException (ioe
);
707 * Returns the value of CLAZZ's private static final field named
708 * `serialPersistentFields'. It performs some sanity checks before
709 * returning the real array. Besides, the returned array is a clean
710 * copy of the original. So it can be modified.
712 * @param clazz Class to retrieve 'serialPersistentFields' from.
713 * @return The content of 'serialPersistentFields'.
715 private ObjectStreamField
[] getSerialPersistentFields (Class clazz
)
716 throws NoSuchFieldException
, IllegalAccessException
718 ObjectStreamField
[] fieldsArray
= null;
719 ObjectStreamField
[] o
;
721 // Use getDeclaredField rather than getField for the same reason
722 // as above in getDefinedSUID.
723 Field f
= clazz
.getDeclaredField("serialPersistentFields");
724 f
.setAccessible(true);
726 int modifiers
= f
.getModifiers();
727 if (!(Modifier
.isStatic(modifiers
)
728 && Modifier
.isFinal(modifiers
)
729 && Modifier
.isPrivate(modifiers
)))
732 o
= (ObjectStreamField
[]) f
.get(null);
737 fieldsArray
= new ObjectStreamField
[o
.length
];
738 System
.arraycopy(o
, 0, fieldsArray
, 0, o
.length
);
743 public static final ObjectStreamField
[] NO_FIELDS
= {};
745 private static Hashtable classLookupTable
= new Hashtable ();
746 private static final NullOutputStream nullOutputStream
= new NullOutputStream ();
747 private static final Comparator interfaceComparator
= new InterfaceComparator ();
748 private static final Comparator memberComparator
= new MemberComparator ();
750 Class
[] writeMethodArgTypes
= { java
.io
.ObjectOutputStream
.class };
752 private ObjectStreamClass superClass
;
758 // this field is package protected so that ObjectInputStream and
759 // ObjectOutputStream can access it directly
760 ObjectStreamField
[] fields
;
762 // these are accessed by ObjectIn/OutputStream
763 int primFieldSize
= -1; // -1 if not yet calculated
764 int objectFieldCount
;
766 boolean isProxyClass
= false;
768 // This is probably not necessary because this class is special cased already
769 // but it will avoid showing up as a discrepancy when comparing SUIDs.
770 private static final long serialVersionUID
= -6120832682080437368L;
775 // interfaces are compared only by name
776 class InterfaceComparator
implements Comparator
778 public int compare (Object o1
, Object o2
)
780 return ((Class
)o1
).getName ().compareTo (((Class
)o2
).getName ());
785 // Members (Methods and Constructors) are compared first by name,
786 // conflicts are resolved by comparing type signatures
787 class MemberComparator
implements Comparator
789 public int compare (Object o1
, Object o2
)
791 Member m1
= (Member
)o1
;
792 Member m2
= (Member
)o2
;
794 int comp
= m1
.getName ().compareTo (m2
.getName ());
797 return TypeSignature
.getEncodingOfMember (m1
).
798 compareTo (TypeSignature
.getEncodingOfMember (m2
));