2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / io / ObjectStreamClass.java
blob9d9d99d33d1e12bf44c64f2243e0ddd3133c4b0e
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)
10 any later version.
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
20 02111-1307 USA.
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
25 combination.
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. */
40 package java.io;
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
63 /**
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
69 will be done.
71 @see java.io.Serializable
73 public static ObjectStreamClass lookup (Class cl)
75 if (cl == null)
76 return null;
77 if (! (Serializable.class).isAssignableFrom (cl))
78 return null;
80 return lookupForClassObject (cl);
83 /**
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)
90 if (cl == null)
91 return null;
93 ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (cl);
95 if (osc != null)
96 return osc;
97 else
99 osc = new ObjectStreamClass (cl);
100 classLookupTable.put (cl, osc);
101 return osc;
107 Returns the name of the class that this
108 <code>ObjectStreamClass</code> represents.
110 public String getName ()
112 return name;
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
121 be found or loaded.
123 @see java.io.ObjectInputStream
125 public Class forClass ()
127 return clazz;
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 ()
140 return uid;
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.
147 // XXX doc
148 public ObjectStreamField[] getFields ()
150 ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
151 System.arraycopy (fields, 0, copy, 0, fields.length);
152 return copy;
156 // XXX doc
157 // Can't do binary search since fields is sorted by name and
158 // primitiveness.
159 public ObjectStreamField getField (String name)
161 for (int i=0; i < fields.length; i++)
162 if (fields[i].getName ().equals (name))
163 return fields[i];
164 return null;
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()
175 * @see #getName()
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);
209 return true;
211 catch (NoSuchMethodException e)
213 return false;
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 ()
240 return superClass;
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);
252 if (osc == null)
253 return new ObjectStreamClass[0];
254 else
256 Vector oscs = new Vector ();
258 while (osc != null)
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);
270 return sorted_oscs;
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_'
279 int getFlags ()
281 return flags;
285 ObjectStreamClass (String name, long uid, byte flags,
286 ObjectStreamField[] fields)
288 this.name = name;
289 this.uid = uid;
290 this.flags = flags;
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
307 this.clazz = cl;
309 long class_uid = getClassUID (cl);
310 if (uid == 0)
311 uid = class_uid;
312 else
314 // Check that the actual UID of the resolved class matches the UID from
315 // the stream.
316 if (uid != class_uid)
318 String msg = cl +
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;
327 calculateOffsets ();
331 ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);
333 if (exportedFields == null)
334 return;
336 ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
337 int i, j, k;
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);
346 i = 0; j = 0; k = 0;
347 while (i < fields.length && j < exportedFields.length)
349 int comp = fields[i].getName().compareTo (exportedFields[j].getName());
350 if (comp < 0)
352 newFieldList[k] = fields[i];
353 fields[i].setPersistent(false);
354 fields[i].setToSet(false);
355 i++;
357 else if (comp > 0)
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);
365 j++;
367 else
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);
374 i++;
375 j++;
377 k++;
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];
387 else
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)
401 return;
403 catch (IllegalAccessException ignore)
405 return;
409 void setSuperclass (ObjectStreamClass osc)
411 superClass = osc;
415 void calculateOffsets ()
417 int i;
418 ObjectStreamField field;
419 primFieldSize = 0;
420 int fcount = fields.length;
421 for (i = 0; i < fcount; ++ i)
423 field = fields[i];
425 if (! field.isPrimitive ())
426 break;
428 field.setOffset (primFieldSize);
429 switch (field.getTypeCode ())
431 case 'B':
432 case 'Z':
433 ++ primFieldSize;
434 break;
435 case 'C':
436 case 'S':
437 primFieldSize += 2;
438 break;
439 case 'I':
440 case 'F':
441 primFieldSize += 4;
442 break;
443 case 'D':
444 case 'J':
445 primFieldSize += 8;
446 break;
450 for (objectFieldCount = 0; i < fcount; ++ i)
451 fields[i].setOffset (objectFieldCount++);
455 private ObjectStreamClass (Class cl)
457 uid = 0;
458 flags = 0;
459 isProxyClass = Proxy.isProxyClass (cl);
461 clazz = cl;
462 name = cl.getName ();
463 setFlags (cl);
464 setFields (cl);
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
498 // clazz.
499 private void setFields (Class cl)
501 if (! isSerializable () || isExternalizable ())
503 fields = NO_FIELDS;
504 return;
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);
519 if (fields != null)
521 Arrays.sort(fields);
522 calculateOffsets();
523 return;
527 catch (NoSuchFieldException ignore)
529 catch (IllegalAccessException ignore)
533 int num_good_fields = 0;
534 Field[] all_fields = cl.getDeclaredFields ();
536 int modifiers;
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;
544 else
545 num_good_fields++;
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 ());
555 to++;
558 Arrays.sort (fields);
559 calculateOffsets ();
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
588 MessageDigest md;
589 try
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.
615 if (! cl.isArray ())
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 ());
623 Field field;
624 Field[] fields = cl.getDeclaredFields ();
625 Arrays.sort (fields, memberComparator);
626 for (int i=0; i < fields.length; i++)
628 field = fields[i];
629 modifiers = field.getModifiers ();
630 if (Modifier.isPrivate (modifiers)
631 && (Modifier.isStatic (modifiers)
632 || Modifier.isTransient (modifiers)))
633 continue;
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))
656 continue;
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
663 data_out.writeUTF (
664 TypeSignature.getEncodingOfConstructor (constructor).replace ('/','.'));
667 Method method;
668 Method[] methods = cl.getDeclaredMethods ();
669 Arrays.sort (methods, memberComparator);
670 for (int i=0; i < methods.length; i++)
672 method = methods[i];
673 modifiers = method.getModifiers ();
674 if (Modifier.isPrivate (modifiers))
675 continue;
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
682 data_out.writeUTF (
683 TypeSignature.getEncodingOfMethod (method).replace ('/', '.'));
686 data_out.close ();
687 byte[] sha = md.digest ();
688 long result = 0;
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);
693 return result;
695 catch (NoSuchAlgorithmException e)
697 throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
698 + cl.getName (), e);
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)))
730 return null;
732 o = (ObjectStreamField[]) f.get(null);
734 if (o == null)
735 return null;
737 fieldsArray = new ObjectStreamField[o.length];
738 System.arraycopy(o, 0, fieldsArray, 0, o.length);
740 return fieldsArray;
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 ();
749 private static final
750 Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
752 private ObjectStreamClass superClass;
753 private Class clazz;
754 private String name;
755 private long uid;
756 private byte flags;
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 ());
796 if (comp == 0)
797 return TypeSignature.getEncodingOfMember (m1).
798 compareTo (TypeSignature.getEncodingOfMember (m2));
799 else
800 return comp;