source: trunk/src/gcc/libjava/java/io/ObjectStreamClass.java@ 2

Last change on this file since 2 was 2, checked in by bird, 23 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 18.2 KB
Line 
1/* ObjectStreamClass.java -- Class used to write class information
2 about serialized objects.
3 Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
2002111-1307 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39
40package java.io;
41
42import java.lang.reflect.Constructor;
43import java.lang.reflect.Field;
44import java.lang.reflect.Member;
45import java.lang.reflect.Method;
46import java.lang.reflect.Modifier;
47import java.security.DigestOutputStream;
48import java.security.MessageDigest;
49import java.security.NoSuchAlgorithmException;
50import java.security.Security;
51import java.util.Arrays;
52import java.util.Comparator;
53import java.util.Hashtable;
54import java.util.Vector;
55import gnu.java.io.NullOutputStream;
56import gnu.java.lang.reflect.TypeSignature;
57import gnu.java.security.provider.Gnu;
58
59
60public class ObjectStreamClass implements Serializable
61{
62 /**
63 Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
64 If <code>cl</code> is null, or is not <code>Serializable</code>,
65 null is returned. <code>ObjectStreamClass</code>'s are memorized;
66 later calls to this method with the same class will return the
67 same <code>ObjectStreamClass</code> object and no recalculation
68 will be done.
69
70 @see java.io.Serializable
71 */
72 public static ObjectStreamClass lookup (Class cl)
73 {
74 if (cl == null)
75 return null;
76 if (! (Serializable.class).isAssignableFrom (cl))
77 return null;
78
79 ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (cl);
80
81 if (osc != null)
82 return osc;
83 else
84 {
85 osc = new ObjectStreamClass (cl);
86 classLookupTable.put (cl, osc);
87 return osc;
88 }
89 }
90
91
92 /**
93 Returns the name of the class that this
94 <code>ObjectStreamClass</code> represents.
95 */
96 public String getName ()
97 {
98 return name;
99 }
100
101
102 /**
103 Returns the class that this <code>ObjectStreamClass</code>
104 represents. Null could be returned if this
105 <code>ObjectStreamClass</code> was read from an
106 <code>ObjectInputStream</code> and the class it represents cannot
107 be found or loaded.
108
109 @see java.io.ObjectInputStream
110 */
111 public Class forClass ()
112 {
113 return clazz;
114 }
115
116
117 /**
118 Returns the serial version stream-unique identifier for the class
119 represented by this <code>ObjectStreamClass</code>. This SUID is
120 either defined by the class as <code>static final long
121 serialVersionUID</code> or is calculated as specified in
122 Javasoft's "Object Serialization Specification" XXX: add reference
123 */
124 public long getSerialVersionUID ()
125 {
126 return uid;
127 }
128
129
130 // Returns the serializable (non-static and non-transient) Fields
131 // of the class represented by this ObjectStreamClass. The Fields
132 // are sorted by name.
133 // XXX doc
134 public ObjectStreamField[] getFields ()
135 {
136 ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
137 System.arraycopy (fields, 0, copy, 0, fields.length);
138 return copy;
139 }
140
141
142 // XXX doc
143 // Can't do binary search since fields is sorted by name and
144 // primitiveness.
145 public ObjectStreamField getField (String name)
146 {
147 for (int i=0; i < fields.length; i++)
148 if (fields[i].getName ().equals (name))
149 return fields[i];
150 return null;
151 }
152
153
154 /**
155 Returns a textual representation of this
156 <code>ObjectStreamClass</code> object including the name of the
157 class it represents as well as that class's serial version
158 stream-unique identifier.
159
160 @see getSerialVersionUID ()
161 @see getName ()
162 */
163 public String toString ()
164 {
165 return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
166 }
167
168
169 // Returns true iff the class that this ObjectStreamClass represents
170 // has the following method:
171 //
172 // private void writeObject (ObjectOutputStream)
173 //
174 // This method is used by the class to override default
175 // serialization behavior.
176 boolean hasWriteMethod ()
177 {
178 return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
179 }
180
181
182 // Returns true iff the class that this ObjectStreamClass represents
183 // implements Serializable but does *not* implement Externalizable.
184 boolean isSerializable ()
185 {
186 return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
187 }
188
189
190 // Returns true iff the class that this ObjectStreamClass represents
191 // implements Externalizable.
192 boolean isExternalizable ()
193 {
194 return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
195 }
196
197
198 // Returns the <code>ObjectStreamClass</code> that represents the
199 // class that is the superclass of the class this
200 // <code>ObjectStreamClass</code> represents. If the superclass is
201 // not Serializable, null is returned.
202 ObjectStreamClass getSuper ()
203 {
204 return superClass;
205 }
206
207
208 // returns an array of ObjectStreamClasses that represent the super
209 // classes of CLAZZ and CLAZZ itself in order from most super to
210 // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ
211 // that is serializable.
212 static ObjectStreamClass[] getObjectStreamClasses (Class clazz)
213 {
214 ObjectStreamClass osc = ObjectStreamClass.lookup (clazz);
215
216 ObjectStreamClass[] ret_val;
217
218 if (osc == null)
219 return new ObjectStreamClass[0];
220 else
221 {
222 Vector oscs = new Vector ();
223
224 while (osc != null)
225 {
226 oscs.addElement (osc);
227 osc = osc.getSuper ();
228 }
229
230 int count = oscs.size ();
231 ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
232
233 for (int i = count - 1; i >= 0; i--)
234 sorted_oscs[ count - i - 1 ] = (ObjectStreamClass)oscs.elementAt (i);
235
236 return sorted_oscs;
237 }
238 }
239
240
241 // Returns an integer that consists of bit-flags that indicate
242 // properties of the class represented by this ObjectStreamClass.
243 // The bit-flags that could be present are those defined in
244 // ObjectStreamConstants that begin with `SC_'
245 int getFlags ()
246 {
247 return flags;
248 }
249
250
251 ObjectStreamClass (String name, long uid, byte flags,
252 ObjectStreamField[] fields)
253 {
254 this.name = name;
255 this.uid = uid;
256 this.flags = flags;
257 this.fields = fields;
258 }
259
260 void setClass (Class cl) throws InvalidClassException
261 {
262 this.clazz = cl;
263 long class_uid = getClassUID (cl);
264 if (uid == 0)
265 {
266 uid = class_uid;
267 return;
268 }
269
270 // Check that the actual UID of the resolved class matches the UID from
271 // the stream.
272 if (uid != class_uid)
273 {
274 String msg = cl +
275 ": Local class not compatible: stream serialVersionUID="
276 + uid + ", local serialVersionUID=" + class_uid;
277 throw new InvalidClassException (msg);
278 }
279 }
280
281 void setSuperclass (ObjectStreamClass osc)
282 {
283 superClass = osc;
284 }
285
286
287 void calculateOffsets ()
288 {
289 int i;
290 ObjectStreamField field;
291 primFieldSize = 0;
292 int fcount = fields.length;
293 for (i = 0; i < fcount; ++ i)
294 {
295 field = fields[i];
296
297 if (! field.isPrimitive ())
298 break;
299
300 field.setOffset (primFieldSize);
301 switch (field.getTypeCode ())
302 {
303 case 'B':
304 case 'Z':
305 ++ primFieldSize;
306 break;
307 case 'C':
308 case 'S':
309 primFieldSize += 2;
310 break;
311 case 'I':
312 case 'F':
313 primFieldSize += 4;
314 break;
315 case 'D':
316 case 'J':
317 primFieldSize += 8;
318 break;
319 }
320 }
321
322 for (objectFieldCount = 0; i < fcount; ++ i)
323 fields[i].setOffset (objectFieldCount++);
324 }
325
326
327 private ObjectStreamClass (Class cl)
328 {
329 uid = 0;
330 flags = 0;
331
332 clazz = cl;
333 name = cl.getName ();
334 setFlags (cl);
335 setFields (cl);
336 uid = getClassUID (cl);
337 superClass = lookup (cl.getSuperclass ());
338 }
339
340
341 // Sets bits in flags according to features of CL.
342 private void setFlags (Class cl)
343 {
344 if ((java.io.Externalizable.class).isAssignableFrom (cl))
345 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
346 else if ((java.io.Serializable.class).isAssignableFrom (cl))
347 // only set this bit if CL is NOT Externalizable
348 flags |= ObjectStreamConstants.SC_SERIALIZABLE;
349
350 try
351 {
352 Method writeMethod = cl.getDeclaredMethod ("writeObject",
353 writeMethodArgTypes);
354 int modifiers = writeMethod.getModifiers ();
355
356 if (writeMethod.getReturnType () == Void.TYPE
357 && Modifier.isPrivate (modifiers)
358 && !Modifier.isStatic (modifiers))
359 flags |= ObjectStreamConstants.SC_WRITE_METHOD;
360 }
361 catch (NoSuchMethodException oh_well)
362 {}
363 }
364
365
366 // Sets fields to be a sorted array of the serializable fields of
367 // clazz.
368 private void setFields (Class cl)
369 {
370 if (! isSerializable () || isExternalizable ())
371 {
372 fields = NO_FIELDS;
373 return;
374 }
375
376 try
377 {
378 Field serialPersistentFields
379 = cl.getDeclaredField ("serialPersistentFields");
380 int modifiers = serialPersistentFields.getModifiers ();
381
382 if (Modifier.isStatic (modifiers)
383 && Modifier.isFinal (modifiers)
384 && Modifier.isPrivate (modifiers))
385 {
386 fields = getSerialPersistentFields (cl);
387 Arrays.sort (fields);
388 calculateOffsets ();
389 return;
390 }
391 }
392 catch (NoSuchFieldException ignore)
393 {}
394
395 int num_good_fields = 0;
396 Field[] all_fields = cl.getDeclaredFields ();
397
398 int modifiers;
399 // set non-serializable fields to null in all_fields
400 for (int i=0; i < all_fields.length; i++)
401 {
402 modifiers = all_fields[i].getModifiers ();
403 if (Modifier.isTransient (modifiers)
404 || Modifier.isStatic (modifiers))
405 all_fields[i] = null;
406 else
407 num_good_fields++;
408 }
409
410 // make a copy of serializable (non-null) fields
411 fields = new ObjectStreamField[ num_good_fields ];
412 for (int from=0, to=0; from < all_fields.length; from++)
413 if (all_fields[from] != null)
414 {
415 Field f = all_fields[from];
416 fields[to] = new ObjectStreamField (f.getName (), f.getType ());
417 to++;
418 }
419
420 Arrays.sort (fields);
421 calculateOffsets ();
422 }
423
424 // Returns the serial version UID defined by class, or if that
425 // isn't present, calculates value of serial version UID.
426 private long getClassUID (Class cl)
427 {
428 try
429 {
430 Field suid = cl.getDeclaredField ("serialVersionUID");
431 int modifiers = suid.getModifiers ();
432
433 if (Modifier.isStatic (modifiers) && Modifier.isFinal (modifiers))
434 return suid.getLong (null);
435 }
436 catch (NoSuchFieldException ignore)
437 {
438 }
439 catch (IllegalAccessException ignore)
440 {
441 }
442
443 // cl didn't define serialVersionUID, so we have to compute it
444 try
445 {
446 MessageDigest md = null;
447 DigestOutputStream digest_out = null;
448 DataOutputStream data_out = null;
449
450 try
451 {
452 md = MessageDigest.getInstance ("SHA");
453 }
454 catch (NoSuchAlgorithmException e)
455 {
456 // If a provider already provides SHA, use it; otherwise, use this.
457 Gnu gnuProvider = new Gnu();
458 Security.addProvider(gnuProvider);
459 md = MessageDigest.getInstance ("SHA");
460 }
461
462 digest_out = new DigestOutputStream (nullOutputStream, md);
463 data_out = new DataOutputStream (digest_out);
464 data_out.writeUTF (cl.getName ());
465
466 int modifiers = cl.getModifiers ();
467 // just look at interesting bits
468 modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
469 | Modifier.INTERFACE | Modifier.PUBLIC);
470 data_out.writeInt (modifiers);
471
472 // Pretend that an array has no interfaces, because when array
473 // serialization was defined (JDK 1.1), arrays didn't have it.
474 if (! cl.isArray ())
475 {
476 Class[] interfaces = cl.getInterfaces ();
477 Arrays.sort (interfaces, interfaceComparator);
478 for (int i=0; i < interfaces.length; i++)
479 data_out.writeUTF (interfaces[i].getName ());
480 }
481
482 Field field;
483 Field[] fields = cl.getDeclaredFields ();
484 Arrays.sort (fields, memberComparator);
485 for (int i=0; i < fields.length; i++)
486 {
487 field = fields[i];
488 modifiers = field.getModifiers ();
489 if (Modifier.isPrivate (modifiers)
490 && (Modifier.isStatic (modifiers)
491 || Modifier.isTransient (modifiers)))
492 continue;
493
494 data_out.writeUTF (field.getName ());
495 data_out.writeInt (modifiers);
496 data_out.writeUTF (TypeSignature.getEncodingOfClass (field.getType ()));
497 }
498
499 // write class initializer method if present
500 boolean has_init;
501 try
502 {
503 has_init = hasClassInitializer (cl);
504 }
505 catch (NoSuchMethodError e)
506 {
507 has_init = false;
508 }
509
510 if (has_init)
511 {
512 data_out.writeUTF ("<clinit>");
513 data_out.writeInt (Modifier.STATIC);
514 data_out.writeUTF ("()V");
515 }
516
517 Constructor constructor;
518 Constructor[] constructors = cl.getDeclaredConstructors ();
519 Arrays.sort (constructors, memberComparator);
520 for (int i=0; i < constructors.length; i++)
521 {
522 constructor = constructors[i];
523 modifiers = constructor.getModifiers ();
524 if (Modifier.isPrivate (modifiers))
525 continue;
526
527 data_out.writeUTF ("<init>");
528 data_out.writeInt (modifiers);
529
530 // the replacement of '/' with '.' was needed to make computed
531 // SUID's agree with those computed by JDK
532 data_out.writeUTF (
533 TypeSignature.getEncodingOfConstructor (constructor).replace ('/','.'));
534 }
535
536 Method method;
537 Method[] methods = cl.getDeclaredMethods ();
538 Arrays.sort (methods, memberComparator);
539 for (int i=0; i < methods.length; i++)
540 {
541 method = methods[i];
542 modifiers = method.getModifiers ();
543 if (Modifier.isPrivate (modifiers))
544 continue;
545
546 data_out.writeUTF (method.getName ());
547 data_out.writeInt (modifiers);
548
549 // the replacement of '/' with '.' was needed to make computed
550 // SUID's agree with those computed by JDK
551 data_out.writeUTF (
552 TypeSignature.getEncodingOfMethod (method).replace ('/', '.'));
553 }
554
555 data_out.close ();
556 byte[] sha = md.digest ();
557 long result = 0;
558 int len = sha.length < 8 ? sha.length : 8;
559 for (int i=0; i < len; i++)
560 result += (long)(sha[i] & 0xFF) << (8 * i);
561
562 return result;
563 }
564 catch (NoSuchAlgorithmException e)
565 {
566 throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
567 + cl.getName ());
568 }
569 catch (IOException ioe)
570 {
571 throw new RuntimeException (ioe.getMessage ());
572 }
573 }
574
575 // Returns the value of CLAZZ's private static final field named
576 // `serialPersistentFields'.
577 private ObjectStreamField[] getSerialPersistentFields (Class clazz)
578 {
579 ObjectStreamField[] o = null;
580 try
581 {
582 // Use getDeclaredField rather than getField for the same reason
583 // as above in getDefinedSUID.
584 Field f = clazz.getDeclaredField ("getSerialPersistentFields");
585 o = (ObjectStreamField[])f.get (null);
586 }
587 catch (java.lang.NoSuchFieldException e)
588 {
589 }
590 catch (java.lang.IllegalAccessException e)
591 {
592 }
593
594 return o;
595 }
596
597
598 // Returns true if CLAZZ has a static class initializer
599 // (a.k.a. <clinit>).
600 //
601 // A NoSuchMethodError is raised if CLAZZ has no such method.
602 private static boolean hasClassInitializer (Class clazz)
603 throws java.lang.NoSuchMethodError
604 {
605 Method m = null;
606
607 try
608 {
609 Class classArgs[] = {};
610 m = clazz.getDeclaredMethod ("<clinit>", classArgs);
611 }
612 catch (java.lang.NoSuchMethodException e)
613 {
614 throw new java.lang.NoSuchMethodError ();
615 }
616
617 return m != null;
618 }
619
620 public static final ObjectStreamField[] NO_FIELDS = {};
621
622 private static Hashtable classLookupTable = new Hashtable ();
623 private static final NullOutputStream nullOutputStream = new NullOutputStream ();
624 private static final Comparator interfaceComparator = new InterfaceComparator ();
625 private static final Comparator memberComparator = new MemberComparator ();
626 private static final
627 Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
628
629 private ObjectStreamClass superClass;
630 private Class clazz;
631 private String name;
632 private long uid;
633 private byte flags;
634
635 // this field is package protected so that ObjectInputStream and
636 // ObjectOutputStream can access it directly
637 ObjectStreamField[] fields;
638
639 // these are accessed by ObjectIn/OutputStream
640 int primFieldSize = -1; // -1 if not yet calculated
641 int objectFieldCount;
642
643 // This is probably not necessary because this class is special cased already
644 // but it will avoid showing up as a discrepancy when comparing SUIDs.
645 private static final long serialVersionUID = -6120832682080437368L;
646}
647
648
649// interfaces are compared only by name
650class InterfaceComparator implements Comparator
651{
652 public int compare (Object o1, Object o2)
653 {
654 return ((Class)o1).getName ().compareTo (((Class)o2).getName ());
655 }
656}
657
658
659// Members (Methods and Constructors) are compared first by name,
660// conflicts are resolved by comparing type signatures
661class MemberComparator implements Comparator
662{
663 public int compare (Object o1, Object o2)
664 {
665 Member m1 = (Member)o1;
666 Member m2 = (Member)o2;
667
668 int comp = m1.getName ().compareTo (m2.getName ());
669
670 if (comp == 0)
671 return TypeSignature.getEncodingOfMember (m1).
672 compareTo (TypeSignature.getEncodingOfMember (m2));
673 else
674 return comp;
675 }
676}
Note: See TracBrowser for help on using the repository browser.