KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > io > ObjectStreamClass


1 /*
2  * @(#)ObjectStreamClass.java 1.138 05/12/01
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.io;
9
10 import java.lang.ref.Reference JavaDoc;
11 import java.lang.ref.ReferenceQueue JavaDoc;
12 import java.lang.ref.SoftReference JavaDoc;
13 import java.lang.ref.WeakReference JavaDoc;
14 import java.lang.reflect.Constructor JavaDoc;
15 import java.lang.reflect.Field JavaDoc;
16 import java.lang.reflect.InvocationTargetException JavaDoc;
17 import java.lang.reflect.Member JavaDoc;
18 import java.lang.reflect.Method JavaDoc;
19 import java.lang.reflect.Modifier JavaDoc;
20 import java.lang.reflect.Proxy JavaDoc;
21 import java.security.AccessController JavaDoc;
22 import java.security.MessageDigest JavaDoc;
23 import java.security.NoSuchAlgorithmException JavaDoc;
24 import java.security.PrivilegedAction JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.Comparator JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Set JavaDoc;
31 import java.util.concurrent.ConcurrentHashMap JavaDoc;
32 import java.util.concurrent.ConcurrentMap JavaDoc;
33 import sun.misc.Unsafe;
34 import sun.reflect.ReflectionFactory;
35
36 /**
37  * Serialization's descriptor for classes. It contains the name and
38  * serialVersionUID of the class. The ObjectStreamClass for a specific class
39  * loaded in this Java VM can be found/created using the lookup method.
40  *
41  * <p>The algorithm to compute the SerialVersionUID is described in
42  * <a HREF="../../../guide/serialization/spec/class.doc4.html">Object
43  * Serialization Specification, Section 4.4, Stream Unique Identifiers</a>.
44  *
45  * @author Mike Warres
46  * @author Roger Riggs
47  * @version 1.98 02/02/00
48  * @see ObjectStreamField
49  * @see <a HREF="../../../guide/serialization/spec/class.doc.html">Object Serialization Specification, Section 4, Class Descriptors</a>
50  * @since JDK1.1
51  */

52 public class ObjectStreamClass implements Serializable JavaDoc {
53
54     /** serialPersistentFields value indicating no serializable fields */
55     public static final ObjectStreamField JavaDoc[] NO_FIELDS =
56     new ObjectStreamField JavaDoc[0];
57     
58     private static final long serialVersionUID = -6120832682080437368L;
59     private static final ObjectStreamField JavaDoc[] serialPersistentFields =
60     NO_FIELDS;
61     
62     /** reflection factory for obtaining serialization constructors */
63     private static final ReflectionFactory reflFactory = (ReflectionFactory)
64     AccessController.doPrivileged(
65         new ReflectionFactory.GetReflectionFactoryAction());
66
67     private static class Caches {
68     /** cache mapping local classes -> descriptors */
69     static final ConcurrentMap JavaDoc<WeakClassKey,Reference JavaDoc<?>> localDescs =
70         new ConcurrentHashMap JavaDoc<WeakClassKey,Reference JavaDoc<?>>();
71
72     /** cache mapping field group/local desc pairs -> field reflectors */
73     static final ConcurrentMap JavaDoc<FieldReflectorKey,Reference JavaDoc<?>> reflectors =
74         new ConcurrentHashMap JavaDoc<FieldReflectorKey,Reference JavaDoc<?>>();
75
76     /** queue for WeakReferences to local classes */
77     private static final ReferenceQueue JavaDoc<Class JavaDoc<?>> localDescsQueue =
78         new ReferenceQueue JavaDoc<Class JavaDoc<?>>();
79     /** queue for WeakReferences to field reflectors keys */
80     private static final ReferenceQueue JavaDoc<Class JavaDoc<?>> reflectorsQueue =
81         new ReferenceQueue JavaDoc<Class JavaDoc<?>>();
82     }
83
84     /** class associated with this descriptor (if any) */
85     private Class JavaDoc cl;
86     /** name of class represented by this descriptor */
87     private String JavaDoc name;
88     /** serialVersionUID of represented class (null if not computed yet) */
89     private volatile Long JavaDoc suid;
90
91     /** true if represents dynamic proxy class */
92     private boolean isProxy;
93     /** true if represents enum type */
94     private boolean isEnum;
95     /** true if represented class implements Serializable */
96     private boolean serializable;
97     /** true if represented class implements Externalizable */
98     private boolean externalizable;
99     /** true if desc has data written by class-defined writeObject method */
100     private boolean hasWriteObjectData;
101     /**
102      * true if desc has externalizable data written in block data format; this
103      * must be true by default to accommodate ObjectInputStream subclasses which
104      * override readClassDescriptor() to return class descriptors obtained from
105      * ObjectStreamClass.lookup() (see 4461737)
106      */

107     private boolean hasBlockExternalData = true;
108
109     /** exception (if any) thrown while attempting to resolve class */
110     private ClassNotFoundException JavaDoc resolveEx;
111     /** exception (if any) to throw if non-enum deserialization attempted */
112     private InvalidClassException JavaDoc deserializeEx;
113     /** exception (if any) to throw if non-enum serialization attempted */
114     private InvalidClassException JavaDoc serializeEx;
115     /** exception (if any) to throw if default serialization attempted */
116     private InvalidClassException JavaDoc defaultSerializeEx;
117
118     /** serializable fields */
119     private ObjectStreamField JavaDoc[] fields;
120     /** aggregate marshalled size of primitive fields */
121     private int primDataSize;
122     /** number of non-primitive fields */
123     private int numObjFields;
124     /** reflector for setting/getting serializable field values */
125     private FieldReflector fieldRefl;
126     /** data layout of serialized objects described by this class desc */
127     private volatile ClassDataSlot[] dataLayout;
128
129     /** serialization-appropriate constructor, or null if none */
130     private Constructor JavaDoc cons;
131     /** class-defined writeObject method, or null if none */
132     private Method JavaDoc writeObjectMethod;
133     /** class-defined readObject method, or null if none */
134     private Method JavaDoc readObjectMethod;
135     /** class-defined readObjectNoData method, or null if none */
136     private Method JavaDoc readObjectNoDataMethod;
137     /** class-defined writeReplace method, or null if none */
138     private Method JavaDoc writeReplaceMethod;
139     /** class-defined readResolve method, or null if none */
140     private Method JavaDoc readResolveMethod;
141
142     /** local class descriptor for represented class (may point to self) */
143     private ObjectStreamClass JavaDoc localDesc;
144     /** superclass descriptor appearing in stream */
145     private ObjectStreamClass JavaDoc superDesc;
146     
147     /**
148      * Initializes native code.
149      */

150     private static native void initNative();
151     static {
152     initNative();
153     }
154
155     /**
156      * Find the descriptor for a class that can be serialized. Creates an
157      * ObjectStreamClass instance if one does not exist yet for class. Null is
158      * returned if the specified class does not implement java.io.Serializable
159      * or java.io.Externalizable.
160      *
161      * @param cl class for which to get the descriptor
162      * @return the class descriptor for the specified class
163      */

164     public static ObjectStreamClass JavaDoc lookup(Class JavaDoc<?> cl) {
165     return lookup(cl, false);
166     }
167     
168     /**
169      * The name of the class described by this descriptor.
170      *
171      * @return a <code>String</code> representing the fully qualified name of
172      * the class
173      */

174     public String JavaDoc getName() {
175     return name;
176     }
177
178     /**
179      * Return the serialVersionUID for this class. The serialVersionUID
180      * defines a set of classes all with the same name that have evolved from a
181      * common root class and agree to be serialized and deserialized using a
182      * common format. NonSerializable classes have a serialVersionUID of 0L.
183      *
184      * @return the SUID of the class described by this descriptor
185      */

186     public long getSerialVersionUID() {
187     // REMIND: synchronize instead of relying on volatile?
188
if (suid == null) {
189         suid = (Long JavaDoc) AccessController.doPrivileged(
190         new PrivilegedAction JavaDoc() {
191             public Object JavaDoc run() {
192             return new Long JavaDoc(computeDefaultSUID(cl));
193             }
194         }
195         );
196     }
197     return suid.longValue();
198     }
199
200     /**
201      * Return the class in the local VM that this version is mapped to. Null
202      * is returned if there is no corresponding local class.
203      *
204      * @return the <code>Class</code> instance that this descriptor represents
205      */

206     public Class JavaDoc<?> forClass() {
207     return cl;
208     }
209     
210     /**
211      * Return an array of the fields of this serializable class.
212      *
213      * @return an array containing an element for each persistent field of
214      * this class. Returns an array of length zero if there are no
215      * fields.
216      * @since 1.2
217      */

218     public ObjectStreamField JavaDoc[] getFields() {
219     return getFields(true);
220     }
221
222     /**
223      * Get the field of this class by name.
224      *
225      * @param name the name of the data field to look for
226      * @return The ObjectStreamField object of the named field or null if
227      * there is no such named field.
228      */

229     public ObjectStreamField JavaDoc getField(String JavaDoc name) {
230     return getField(name, null);
231     }
232
233     /**
234      * Return a string describing this ObjectStreamClass.
235      */

236     public String JavaDoc toString() {
237     return name + ": static final long serialVersionUID = " +
238         getSerialVersionUID() + "L;";
239     }
240     
241     /**
242      * Looks up and returns class descriptor for given class, or null if class
243      * is non-serializable and "all" is set to false.
244      *
245      * @param cl class to look up
246      * @param all if true, return descriptors for all classes; if false, only
247      * return descriptors for serializable classes
248      */

249     static ObjectStreamClass JavaDoc lookup(Class JavaDoc cl, boolean all) {
250     if (!(all || Serializable JavaDoc.class.isAssignableFrom(cl))) {
251         return null;
252     }
253     processQueue(Caches.localDescsQueue, Caches.localDescs);
254     WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
255     Reference JavaDoc<?> ref = Caches.localDescs.get(key);
256     Object JavaDoc entry = null;
257     if (ref != null) {
258         entry = ref.get();
259     }
260     EntryFuture future = null;
261     if (entry == null) {
262         EntryFuture newEntry = new EntryFuture();
263         Reference JavaDoc<?> newRef = new SoftReference JavaDoc<EntryFuture>(newEntry);
264         do {
265         if (ref != null) {
266             Caches.localDescs.remove(key, ref);
267         }
268         ref = Caches.localDescs.putIfAbsent(key, newRef);
269         if (ref != null) {
270             entry = ref.get();
271         }
272         } while (ref != null && entry == null);
273         if (entry == null) {
274         future = newEntry;
275         }
276     }
277     
278     if (entry instanceof ObjectStreamClass JavaDoc) { // check common case first
279
return (ObjectStreamClass JavaDoc) entry;
280     }
281     if (entry instanceof EntryFuture) {
282         future = (EntryFuture) entry;
283         if (future.getOwner() == Thread.currentThread()) {
284         /*
285          * Handle nested call situation described by 4803747: waiting
286          * for future value to be set by a lookup() call further up the
287          * stack will result in deadlock, so calculate and set the
288          * future value here instead.
289          */

290         entry = null;
291         } else {
292         entry = future.get();
293         }
294     }
295     if (entry == null) {
296         try {
297         entry = new ObjectStreamClass JavaDoc(cl);
298         } catch (Throwable JavaDoc th) {
299         entry = th;
300         }
301         if (future.set(entry)) {
302         Caches.localDescs.put(key, new SoftReference JavaDoc<Object JavaDoc>(entry));
303         } else {
304         // nested lookup call already set future
305
entry = future.get();
306         }
307     }
308     
309     if (entry instanceof ObjectStreamClass JavaDoc) {
310         return (ObjectStreamClass JavaDoc) entry;
311     } else if (entry instanceof RuntimeException JavaDoc) {
312         throw (RuntimeException JavaDoc) entry;
313     } else if (entry instanceof Error JavaDoc) {
314         throw (Error JavaDoc) entry;
315     } else {
316         throw new InternalError JavaDoc("unexpected entry: " + entry);
317     }
318     }
319
320     /**
321      * Placeholder used in class descriptor and field reflector lookup tables
322      * for an entry in the process of being initialized. (Internal) callers
323      * which receive an EntryFuture belonging to another thread as the result
324      * of a lookup should call the get() method of the EntryFuture; this will
325      * return the actual entry once it is ready for use and has been set(). To
326      * conserve objects, EntryFutures synchronize on themselves.
327      */

328     private static class EntryFuture {
329     
330     private static final Object JavaDoc unset = new Object JavaDoc();
331     private final Thread JavaDoc owner = Thread.currentThread();
332     private Object JavaDoc entry = unset;
333
334     /**
335      * Attempts to set the value contained by this EntryFuture. If the
336      * EntryFuture's value has not been set already, then the value is
337      * saved, any callers blocked in the get() method are notified, and
338      * true is returned. If the value has already been set, then no saving
339      * or notification occurs, and false is returned.
340      */

341     synchronized boolean set(Object JavaDoc entry) {
342         if (this.entry != unset) {
343         return false;
344         }
345         this.entry = entry;
346         notifyAll();
347         return true;
348     }
349     
350     /**
351      * Returns the value contained by this EntryFuture, blocking if
352      * necessary until a value is set.
353      */

354     synchronized Object JavaDoc get() {
355         boolean interrupted = false;
356         while (entry == unset) {
357         try {
358             wait();
359         } catch (InterruptedException JavaDoc ex) {
360             interrupted = true;
361         }
362         }
363         if (interrupted) {
364         AccessController.doPrivileged(
365             new PrivilegedAction JavaDoc() {
366             public Object JavaDoc run() {
367                 Thread.currentThread().interrupt();
368                 return null;
369             }
370             }
371         );
372         }
373         return entry;
374     }
375
376     /**
377      * Returns the thread that created this EntryFuture.
378      */

379     Thread JavaDoc getOwner() {
380         return owner;
381     }
382     }
383
384     /**
385      * Creates local class descriptor representing given class.
386      */

387     private ObjectStreamClass(final Class JavaDoc cl) {
388     this.cl = cl;
389     name = cl.getName();
390     isProxy = Proxy.isProxyClass(cl);
391     isEnum = Enum JavaDoc.class.isAssignableFrom(cl);
392     serializable = Serializable JavaDoc.class.isAssignableFrom(cl);
393     externalizable = Externalizable JavaDoc.class.isAssignableFrom(cl);
394
395     Class JavaDoc superCl = cl.getSuperclass();
396     superDesc = (superCl != null) ? lookup(superCl, false) : null;
397     localDesc = this;
398
399     if (serializable) {
400         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
401         public Object JavaDoc run() {
402             if (isEnum) {
403             suid = new Long JavaDoc(0);
404             fields = NO_FIELDS;
405             return null;
406             }
407
408             suid = getDeclaredSUID(cl);
409             try {
410             fields = getSerialFields(cl);
411             computeFieldOffsets();
412             } catch (InvalidClassException JavaDoc e) {
413             serializeEx = deserializeEx = e;
414             fields = NO_FIELDS;
415             }
416             
417             if (externalizable) {
418             cons = getExternalizableConstructor(cl);
419             } else {
420             cons = getSerializableConstructor(cl);
421             writeObjectMethod = getPrivateMethod(cl, "writeObject",
422                 new Class JavaDoc[] { ObjectOutputStream JavaDoc.class },
423                 Void.TYPE);
424             readObjectMethod = getPrivateMethod(cl, "readObject",
425                 new Class JavaDoc[] { ObjectInputStream JavaDoc.class },
426                 Void.TYPE);
427             readObjectNoDataMethod = getPrivateMethod(
428                 cl, "readObjectNoData",
429                 new Class JavaDoc[0], Void.TYPE);
430             hasWriteObjectData = (writeObjectMethod != null);
431             }
432             writeReplaceMethod = getInheritableMethod(
433             cl, "writeReplace", new Class JavaDoc[0], Object JavaDoc.class);
434             readResolveMethod = getInheritableMethod(
435             cl, "readResolve", new Class JavaDoc[0], Object JavaDoc.class);
436             return null;
437         }
438         });
439     } else {
440         suid = new Long JavaDoc(0);
441         fields = NO_FIELDS;
442     }
443
444     try {
445         fieldRefl = getReflector(fields, this);
446     } catch (InvalidClassException JavaDoc ex) {
447         // field mismatches impossible when matching local fields vs. self
448
throw new InternalError JavaDoc();
449     }
450
451     if (deserializeEx == null) {
452         if (isEnum) {
453         deserializeEx = new InvalidClassException JavaDoc(name, "enum type");
454         } else if (cons == null) {
455         deserializeEx = new InvalidClassException JavaDoc(
456             name, "no valid constructor");
457         }
458     }
459     for (int i = 0; i < fields.length; i++) {
460         if (fields[i].getField() == null) {
461         defaultSerializeEx = new InvalidClassException JavaDoc(
462             name, "unmatched serializable field(s) declared");
463         }
464     }
465     }
466
467     /**
468      * Creates blank class descriptor which should be initialized via a
469      * subsequent call to initProxy(), initNonProxy() or readNonProxy().
470      */

471     ObjectStreamClass() {
472     }
473     
474     /**
475      * Initializes class descriptor representing a proxy class.
476      */

477     void initProxy(Class JavaDoc cl,
478            ClassNotFoundException JavaDoc resolveEx,
479            ObjectStreamClass JavaDoc superDesc)
480     throws InvalidClassException JavaDoc
481     {
482     this.cl = cl;
483     this.resolveEx = resolveEx;
484     this.superDesc = superDesc;
485     isProxy = true;
486     serializable = true;
487     suid = new Long JavaDoc(0);
488     fields = NO_FIELDS;
489     
490     if (cl != null) {
491         localDesc = lookup(cl, true);
492         if (!localDesc.isProxy) {
493         throw new InvalidClassException JavaDoc(
494             "cannot bind proxy descriptor to a non-proxy class");
495         }
496         name = localDesc.name;
497         externalizable = localDesc.externalizable;
498         cons = localDesc.cons;
499         writeReplaceMethod = localDesc.writeReplaceMethod;
500         readResolveMethod = localDesc.readResolveMethod;
501         deserializeEx = localDesc.deserializeEx;
502     }
503     fieldRefl = getReflector(fields, localDesc);
504     }
505     
506     /**
507      * Initializes class descriptor representing a non-proxy class.
508      */

509     void initNonProxy(ObjectStreamClass JavaDoc model,
510               Class JavaDoc cl,
511               ClassNotFoundException JavaDoc resolveEx,
512               ObjectStreamClass JavaDoc superDesc)
513     throws InvalidClassException JavaDoc
514     {
515     this.cl = cl;
516     this.resolveEx = resolveEx;
517     this.superDesc = superDesc;
518     name = model.name;
519     suid = new Long JavaDoc(model.getSerialVersionUID());
520     isProxy = false;
521     isEnum = model.isEnum;
522     serializable = model.serializable;
523     externalizable = model.externalizable;
524     hasBlockExternalData = model.hasBlockExternalData;
525     hasWriteObjectData = model.hasWriteObjectData;
526     fields = model.fields;
527     primDataSize = model.primDataSize;
528     numObjFields = model.numObjFields;
529     
530     if (cl != null) {
531         localDesc = lookup(cl, true);
532         if (localDesc.isProxy) {
533         throw new InvalidClassException JavaDoc(
534             "cannot bind non-proxy descriptor to a proxy class");
535         }
536         if (isEnum != localDesc.isEnum) {
537         throw new InvalidClassException JavaDoc(isEnum ?
538             "cannot bind enum descriptor to a non-enum class" :
539             "cannot bind non-enum descriptor to an enum class");
540         }
541         
542         if (serializable == localDesc.serializable &&
543         !cl.isArray() &&
544         suid.longValue() != localDesc.getSerialVersionUID())
545         {
546         throw new InvalidClassException JavaDoc(localDesc.name,
547             "local class incompatible: " +
548             "stream classdesc serialVersionUID = " + suid +
549             ", local class serialVersionUID = " +
550             localDesc.getSerialVersionUID());
551         }
552         
553         if (!classNamesEqual(name, localDesc.name)) {
554         throw new InvalidClassException JavaDoc(localDesc.name,
555             "local class name incompatible with stream class " +
556             "name \"" + name + "\"");
557         }
558         
559         if (!isEnum) {
560         if ((serializable == localDesc.serializable) &&
561             (externalizable != localDesc.externalizable))
562         {
563             throw new InvalidClassException JavaDoc(localDesc.name,
564             "Serializable incompatible with Externalizable");
565         }
566
567         if ((serializable != localDesc.serializable) ||
568             (externalizable != localDesc.externalizable) ||
569             !(serializable || externalizable))
570         {
571             deserializeEx = new InvalidClassException JavaDoc(localDesc.name,
572             "class invalid for deserialization");
573         }
574         }
575         
576         cons = localDesc.cons;
577         writeObjectMethod = localDesc.writeObjectMethod;
578         readObjectMethod = localDesc.readObjectMethod;
579         readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
580         writeReplaceMethod = localDesc.writeReplaceMethod;
581         readResolveMethod = localDesc.readResolveMethod;
582         if (deserializeEx == null) {
583         deserializeEx = localDesc.deserializeEx;
584         }
585     }
586     fieldRefl = getReflector(fields, localDesc);
587     // reassign to matched fields so as to reflect local unshared settings
588
fields = fieldRefl.getFields();
589     }
590     
591     /**
592      * Reads non-proxy class descriptor information from given input stream.
593      * The resulting class descriptor is not fully functional; it can only be
594      * used as input to the ObjectInputStream.resolveClass() and
595      * ObjectStreamClass.initNonProxy() methods.
596      */

597     void readNonProxy(ObjectInputStream JavaDoc in)
598     throws IOException JavaDoc, ClassNotFoundException JavaDoc
599     {
600     name = in.readUTF();
601     suid = new Long JavaDoc(in.readLong());
602     isProxy = false;
603
604     byte flags = in.readByte();
605     hasWriteObjectData =
606         ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
607     hasBlockExternalData =
608         ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
609     externalizable =
610         ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
611     boolean sflag =
612         ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
613     if (externalizable && sflag) {
614         throw new InvalidClassException JavaDoc(
615         name, "serializable and externalizable flags conflict");
616     }
617     serializable = externalizable || sflag;
618     isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
619     if (isEnum && suid.longValue() != 0L) {
620         throw new InvalidClassException JavaDoc(name,
621         "enum descriptor has non-zero serialVersionUID: " + suid);
622     }
623     
624     int numFields = in.readShort();
625     if (isEnum && numFields != 0) {
626         throw new InvalidClassException JavaDoc(name,
627         "enum descriptor has non-zero field count: " + numFields);
628     }
629     fields = (numFields > 0) ?
630         new ObjectStreamField JavaDoc[numFields] : NO_FIELDS;
631     for (int i = 0; i < numFields; i++) {
632         char tcode = (char) in.readByte();
633         String JavaDoc fname = in.readUTF();
634         String JavaDoc signature = ((tcode == 'L') || (tcode == '[')) ?
635         in.readTypeString() : new String JavaDoc(new char[] { tcode });
636         try {
637         fields[i] = new ObjectStreamField JavaDoc(fname, signature, false);
638         } catch (RuntimeException JavaDoc e) {
639         throw (IOException JavaDoc) new InvalidClassException JavaDoc(name,
640             "invalid descriptor for field " + fname).initCause(e);
641         }
642     }
643     computeFieldOffsets();
644     }
645
646     /**
647      * Writes non-proxy class descriptor information to given output stream.
648      */

649     void writeNonProxy(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
650     out.writeUTF(name);
651     out.writeLong(getSerialVersionUID());
652
653     byte flags = 0;
654     if (externalizable) {
655         flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
656         int protocol = out.getProtocolVersion();
657         if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
658         flags |= ObjectStreamConstants.SC_BLOCK_DATA;
659         }
660     } else if (serializable) {
661         flags |= ObjectStreamConstants.SC_SERIALIZABLE;
662     }
663     if (hasWriteObjectData) {
664         flags |= ObjectStreamConstants.SC_WRITE_METHOD;
665     }
666     if (isEnum) {
667         flags |= ObjectStreamConstants.SC_ENUM;
668     }
669     out.writeByte(flags);
670     
671     out.writeShort(fields.length);
672     for (int i = 0; i < fields.length; i++) {
673         ObjectStreamField JavaDoc f = fields[i];
674         out.writeByte(f.getTypeCode());
675         out.writeUTF(f.getName());
676         if (!f.isPrimitive()) {
677         out.writeTypeString(f.getTypeString());
678         }
679     }
680     }
681     
682     /**
683      * Returns ClassNotFoundException (if any) thrown while attempting to
684      * resolve local class corresponding to this class descriptor.
685      */

686     ClassNotFoundException JavaDoc getResolveException() {
687     return resolveEx;
688     }
689
690     /**
691      * Throws an InvalidClassException if object instances referencing this
692      * class descriptor should not be allowed to deserialize. This method does
693      * not apply to deserialization of enum constants.
694      */

695     void checkDeserialize() throws InvalidClassException JavaDoc {
696     if (deserializeEx != null) {
697         throw deserializeEx;
698     }
699     }
700     
701     /**
702      * Throws an InvalidClassException if objects whose class is represented by
703      * this descriptor should not be allowed to serialize. This method does
704      * not apply to serialization of enum constants.
705      */

706     void checkSerialize() throws InvalidClassException JavaDoc {
707     if (serializeEx != null) {
708         throw serializeEx;
709     }
710     }
711
712     /**
713      * Throws an InvalidClassException if objects whose class is represented by
714      * this descriptor should not be permitted to use default serialization
715      * (e.g., if the class declares serializable fields that do not correspond
716      * to actual fields, and hence must use the GetField API). This method
717      * does not apply to deserialization of enum constants.
718      */

719     void checkDefaultSerialize() throws InvalidClassException JavaDoc {
720     if (defaultSerializeEx != null) {
721         throw defaultSerializeEx;
722     }
723     }
724
725     /**
726      * Returns superclass descriptor. Note that on the receiving side, the
727      * superclass descriptor may be bound to a class that is not a superclass
728      * of the subclass descriptor's bound class.
729      */

730     ObjectStreamClass JavaDoc getSuperDesc() {
731     return superDesc;
732     }
733     
734     /**
735      * Returns the "local" class descriptor for the class associated with this
736      * class descriptor (i.e., the result of
737      * ObjectStreamClass.lookup(this.forClass())) or null if there is no class
738      * associated with this descriptor.
739      */

740     ObjectStreamClass JavaDoc getLocalDesc() {
741     return localDesc;
742     }
743
744     /**
745      * Returns arrays of ObjectStreamFields representing the serializable
746      * fields of the represented class. If copy is true, a clone of this class
747      * descriptor's field array is returned, otherwise the array itself is
748      * returned.
749      */

750     ObjectStreamField JavaDoc[] getFields(boolean copy) {
751     return copy ? (ObjectStreamField JavaDoc[]) fields.clone() : fields;
752     }
753     
754     /**
755      * Looks up a serializable field of the represented class by name and type.
756      * A specified type of null matches all types, Object.class matches all
757      * non-primitive types, and any other non-null type matches assignable
758      * types only. Returns matching field, or null if no match found.
759      */

760     ObjectStreamField JavaDoc getField(String JavaDoc name, Class JavaDoc type) {
761     for (int i = 0; i < fields.length; i++) {
762         ObjectStreamField JavaDoc f = fields[i];
763         if (f.getName().equals(name)) {
764         if (type == null ||
765             (type == Object JavaDoc.class && !f.isPrimitive()))
766         {
767             return f;
768         }
769         Class JavaDoc ftype = f.getType();
770         if (ftype != null && type.isAssignableFrom(ftype)) {
771             return f;
772         }
773         }
774     }
775     return null;
776     }
777
778     /**
779      * Returns true if class descriptor represents a dynamic proxy class, false
780      * otherwise.
781      */

782     boolean isProxy() {
783     return isProxy;
784     }
785     
786     /**
787      * Returns true if class descriptor represents an enum type, false
788      * otherwise.
789      */

790     boolean isEnum() {
791     return isEnum;
792     }
793     
794     /**
795      * Returns true if represented class implements Externalizable, false
796      * otherwise.
797      */

798     boolean isExternalizable() {
799     return externalizable;
800     }
801     
802     /**
803      * Returns true if represented class implements Serializable, false
804      * otherwise.
805      */

806     boolean isSerializable() {
807     return serializable;
808     }
809
810     /**
811      * Returns true if class descriptor represents externalizable class that
812      * has written its data in 1.2 (block data) format, false otherwise.
813      */

814     boolean hasBlockExternalData() {
815     return hasBlockExternalData;
816     }
817     
818     /**
819      * Returns true if class descriptor represents serializable (but not
820      * externalizable) class which has written its data via a custom
821      * writeObject() method, false otherwise.
822      */

823     boolean hasWriteObjectData() {
824     return hasWriteObjectData;
825     }
826     
827     /**
828      * Returns true if represented class is serializable/externalizable and can
829      * be instantiated by the serialization runtime--i.e., if it is
830      * externalizable and defines a public no-arg constructor, or if it is
831      * non-externalizable and its first non-serializable superclass defines an
832      * accessible no-arg constructor. Otherwise, returns false.
833      */

834     boolean isInstantiable() {
835     return (cons != null);
836     }
837     
838     /**
839      * Returns true if represented class is serializable (but not
840      * externalizable) and defines a conformant writeObject method. Otherwise,
841      * returns false.
842      */

843     boolean hasWriteObjectMethod() {
844     return (writeObjectMethod != null);
845     }
846     
847     /**
848      * Returns true if represented class is serializable (but not
849      * externalizable) and defines a conformant readObject method. Otherwise,
850      * returns false.
851      */

852     boolean hasReadObjectMethod() {
853     return (readObjectMethod != null);
854     }
855     
856     /**
857      * Returns true if represented class is serializable (but not
858      * externalizable) and defines a conformant readObjectNoData method.
859      * Otherwise, returns false.
860      */

861     boolean hasReadObjectNoDataMethod() {
862     return (readObjectNoDataMethod != null);
863     }
864     
865     /**
866      * Returns true if represented class is serializable or externalizable and
867      * defines a conformant writeReplace method. Otherwise, returns false.
868      */

869     boolean hasWriteReplaceMethod() {
870     return (writeReplaceMethod != null);
871     }
872     
873     /**
874      * Returns true if represented class is serializable or externalizable and
875      * defines a conformant readResolve method. Otherwise, returns false.
876      */

877     boolean hasReadResolveMethod() {
878     return (readResolveMethod != null);
879     }
880
881     /**
882      * Creates a new instance of the represented class. If the class is
883      * externalizable, invokes its public no-arg constructor; otherwise, if the
884      * class is serializable, invokes the no-arg constructor of the first
885      * non-serializable superclass. Throws UnsupportedOperationException if
886      * this class descriptor is not associated with a class, if the associated
887      * class is non-serializable or if the appropriate no-arg constructor is
888      * inaccessible/unavailable.
889      */

890     Object JavaDoc newInstance()
891     throws InstantiationException JavaDoc, InvocationTargetException JavaDoc,
892            UnsupportedOperationException JavaDoc
893     {
894     if (cons != null) {
895         try {
896         return cons.newInstance(null);
897         } catch (IllegalAccessException JavaDoc ex) {
898         // should not occur, as access checks have been suppressed
899
throw new InternalError JavaDoc();
900         }
901     } else {
902         throw new UnsupportedOperationException JavaDoc();
903     }
904     }
905            
906     /**
907      * Invokes the writeObject method of the represented serializable class.
908      * Throws UnsupportedOperationException if this class descriptor is not
909      * associated with a class, or if the class is externalizable,
910      * non-serializable or does not define writeObject.
911      */

912     void invokeWriteObject(Object JavaDoc obj, ObjectOutputStream JavaDoc out)
913     throws IOException JavaDoc, UnsupportedOperationException JavaDoc
914     {
915     if (writeObjectMethod != null) {
916         try {
917         writeObjectMethod.invoke(obj, new Object JavaDoc[]{ out });
918         } catch (InvocationTargetException JavaDoc ex) {
919         Throwable JavaDoc th = ex.getTargetException();
920         if (th instanceof IOException JavaDoc) {
921             throw (IOException JavaDoc) th;
922         } else {
923             throwMiscException(th);
924         }
925         } catch (IllegalAccessException JavaDoc ex) {
926         // should not occur, as access checks have been suppressed
927
throw new InternalError JavaDoc();
928         }
929     } else {
930         throw new UnsupportedOperationException JavaDoc();
931     }
932     }
933     
934     /**
935      * Invokes the readObject method of the represented serializable class.
936      * Throws UnsupportedOperationException if this class descriptor is not
937      * associated with a class, or if the class is externalizable,
938      * non-serializable or does not define readObject.
939      */

940     void invokeReadObject(Object JavaDoc obj, ObjectInputStream JavaDoc in)
941     throws ClassNotFoundException JavaDoc, IOException JavaDoc,
942            UnsupportedOperationException JavaDoc
943     {
944     if (readObjectMethod != null) {
945         try {
946         readObjectMethod.invoke(obj, new Object JavaDoc[]{ in });
947         } catch (InvocationTargetException JavaDoc ex) {
948         Throwable JavaDoc th = ex.getTargetException();
949         if (th instanceof ClassNotFoundException JavaDoc) {
950             throw (ClassNotFoundException JavaDoc) th;
951         } else if (th instanceof IOException JavaDoc) {
952             throw (IOException JavaDoc) th;
953         } else {
954             throwMiscException(th);
955         }
956         } catch (IllegalAccessException JavaDoc ex) {
957         // should not occur, as access checks have been suppressed
958
throw new InternalError JavaDoc();
959         }
960     } else {
961         throw new UnsupportedOperationException JavaDoc();
962     }
963     }
964
965     /**
966      * Invokes the readObjectNoData method of the represented serializable
967      * class. Throws UnsupportedOperationException if this class descriptor is
968      * not associated with a class, or if the class is externalizable,
969      * non-serializable or does not define readObjectNoData.
970      */

971     void invokeReadObjectNoData(Object JavaDoc obj)
972     throws IOException JavaDoc, UnsupportedOperationException JavaDoc
973     {
974     if (readObjectNoDataMethod != null) {
975         try {
976         readObjectNoDataMethod.invoke(obj, null);
977         } catch (InvocationTargetException JavaDoc ex) {
978         Throwable JavaDoc th = ex.getTargetException();
979         if (th instanceof ObjectStreamException JavaDoc) {
980             throw (ObjectStreamException JavaDoc) th;
981         } else {
982             throwMiscException(th);
983         }
984         } catch (IllegalAccessException JavaDoc ex) {
985         // should not occur, as access checks have been suppressed
986
throw new InternalError JavaDoc();
987         }
988     } else {
989         throw new UnsupportedOperationException JavaDoc();
990     }
991     }
992
993     /**
994      * Invokes the writeReplace method of the represented serializable class and
995      * returns the result. Throws UnsupportedOperationException if this class
996      * descriptor is not associated with a class, or if the class is
997      * non-serializable or does not define writeReplace.
998      */

999     Object JavaDoc invokeWriteReplace(Object JavaDoc obj)
1000    throws IOException JavaDoc, UnsupportedOperationException JavaDoc
1001    {
1002    if (writeReplaceMethod != null) {
1003        try {
1004        return writeReplaceMethod.invoke(obj, null);
1005        } catch (InvocationTargetException JavaDoc ex) {
1006        Throwable JavaDoc th = ex.getTargetException();
1007        if (th instanceof ObjectStreamException JavaDoc) {
1008            throw (ObjectStreamException JavaDoc) th;
1009        } else {
1010            throwMiscException(th);
1011            throw new InternalError JavaDoc(); // never reached
1012
}
1013        } catch (IllegalAccessException JavaDoc ex) {
1014        // should not occur, as access checks have been suppressed
1015
throw new InternalError JavaDoc();
1016        }
1017    } else {
1018        throw new UnsupportedOperationException JavaDoc();
1019    }
1020    }
1021
1022    /**
1023     * Invokes the readResolve method of the represented serializable class and
1024     * returns the result. Throws UnsupportedOperationException if this class
1025     * descriptor is not associated with a class, or if the class is
1026     * non-serializable or does not define readResolve.
1027     */

1028    Object JavaDoc invokeReadResolve(Object JavaDoc obj)
1029    throws IOException JavaDoc, UnsupportedOperationException JavaDoc
1030    {
1031    if (readResolveMethod != null) {
1032        try {
1033        return readResolveMethod.invoke(obj, null);
1034        } catch (InvocationTargetException JavaDoc ex) {
1035        Throwable JavaDoc th = ex.getTargetException();
1036        if (th instanceof ObjectStreamException JavaDoc) {
1037            throw (ObjectStreamException JavaDoc) th;
1038        } else {
1039            throwMiscException(th);
1040            throw new InternalError JavaDoc(); // never reached
1041
}
1042        } catch (IllegalAccessException JavaDoc ex) {
1043        // should not occur, as access checks have been suppressed
1044
throw new InternalError JavaDoc();
1045        }
1046    } else {
1047        throw new UnsupportedOperationException JavaDoc();
1048    }
1049    }
1050
1051    /**
1052     * Class representing the portion of an object's serialized form allotted
1053     * to data described by a given class descriptor. If "hasData" is false,
1054     * the object's serialized form does not contain data associated with the
1055     * class descriptor.
1056     */

1057    static class ClassDataSlot {
1058
1059    /** class descriptor "occupying" this slot */
1060    final ObjectStreamClass JavaDoc desc;
1061    /** true if serialized form includes data for this slot's descriptor */
1062    final boolean hasData;
1063    
1064    ClassDataSlot(ObjectStreamClass JavaDoc desc, boolean hasData) {
1065        this.desc = desc;
1066        this.hasData = hasData;
1067    }
1068    }
1069
1070    /**
1071     * Returns array of ClassDataSlot instances representing the data layout
1072     * (including superclass data) for serialized objects described by this
1073     * class descriptor. ClassDataSlots are ordered by inheritance with those
1074     * containing "higher" superclasses appearing first. The final
1075     * ClassDataSlot contains a reference to this descriptor.
1076     */

1077    ClassDataSlot[] getClassDataLayout() throws InvalidClassException JavaDoc {
1078    // REMIND: synchronize instead of relying on volatile?
1079
if (dataLayout == null) {
1080        dataLayout = getClassDataLayout0();
1081    }
1082    return dataLayout;
1083    }
1084    
1085    private ClassDataSlot[] getClassDataLayout0()
1086    throws InvalidClassException JavaDoc
1087    {
1088    ArrayList JavaDoc slots = new ArrayList JavaDoc();
1089    Class JavaDoc start = cl, end = cl;
1090    
1091    // locate closest non-serializable superclass
1092
while (end != null && Serializable JavaDoc.class.isAssignableFrom(end)) {
1093        end = end.getSuperclass();
1094    }
1095
1096    for (ObjectStreamClass JavaDoc d = this; d != null; d = d.superDesc) {
1097        
1098        // search up inheritance hierarchy for class with matching name
1099
String JavaDoc searchName = (d.cl != null) ? d.cl.getName() : d.name;
1100        Class JavaDoc match = null;
1101        for (Class JavaDoc c = start; c != end; c = c.getSuperclass()) {
1102        if (searchName.equals(c.getName())) {
1103            match = c;
1104            break;
1105        }
1106        }
1107
1108        // add "no data" slot for each unmatched class below match
1109
if (match != null) {
1110        for (Class JavaDoc c = start; c != match; c = c.getSuperclass()) {
1111            slots.add(new ClassDataSlot(
1112            ObjectStreamClass.lookup(c, true), false));
1113        }
1114        start = match.getSuperclass();
1115        }
1116        
1117        // record descriptor/class pairing
1118
slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1119    }
1120    
1121    // add "no data" slot for any leftover unmatched classes
1122
for (Class JavaDoc c = start; c != end; c = c.getSuperclass()) {
1123        slots.add(new ClassDataSlot(
1124        ObjectStreamClass.lookup(c, true), false));
1125    }
1126
1127    // order slots from superclass -> subclass
1128
Collections.reverse(slots);
1129    return (ClassDataSlot[])
1130        slots.toArray(new ClassDataSlot[slots.size()]);
1131    }
1132    
1133    /**
1134     * Returns aggregate size (in bytes) of marshalled primitive field values
1135     * for represented class.
1136     */

1137    int getPrimDataSize() {
1138    return primDataSize;
1139    }
1140    
1141    /**
1142     * Returns number of non-primitive serializable fields of represented
1143     * class.
1144     */

1145    int getNumObjFields() {
1146    return numObjFields;
1147    }
1148    
1149    /**
1150     * Fetches the serializable primitive field values of object obj and
1151     * marshals them into byte array buf starting at offset 0. It is the
1152     * responsibility of the caller to ensure that obj is of the proper type if
1153     * non-null.
1154     */

1155    void getPrimFieldValues(Object JavaDoc obj, byte[] buf) {
1156    fieldRefl.getPrimFieldValues(obj, buf);
1157    }
1158
1159    /**
1160     * Sets the serializable primitive fields of object obj using values
1161     * unmarshalled from byte array buf starting at offset 0. It is the
1162     * responsibility of the caller to ensure that obj is of the proper type if
1163     * non-null.
1164     */

1165    void setPrimFieldValues(Object JavaDoc obj, byte[] buf) {
1166    fieldRefl.setPrimFieldValues(obj, buf);
1167    }
1168    
1169    /**
1170     * Fetches the serializable object field values of object obj and stores
1171     * them in array vals starting at offset 0. It is the responsibility of
1172     * the caller to ensure that obj is of the proper type if non-null.
1173     */

1174    void getObjFieldValues(Object JavaDoc obj, Object JavaDoc[] vals) {
1175    fieldRefl.getObjFieldValues(obj, vals);
1176    }
1177    
1178    /**
1179     * Sets the serializable object fields of object obj using values from
1180     * array vals starting at offset 0. It is the responsibility of the caller
1181     * to ensure that obj is of the proper type if non-null.
1182     */

1183    void setObjFieldValues(Object JavaDoc obj, Object JavaDoc[] vals) {
1184    fieldRefl.setObjFieldValues(obj, vals);
1185    }
1186    
1187    /**
1188     * Calculates and sets serializable field offsets, as well as primitive
1189     * data size and object field count totals. Throws InvalidClassException
1190     * if fields are illegally ordered.
1191     */

1192    private void computeFieldOffsets() throws InvalidClassException JavaDoc {
1193    primDataSize = 0;
1194    numObjFields = 0;
1195    int firstObjIndex = -1;
1196
1197    for (int i = 0; i < fields.length; i++) {
1198        ObjectStreamField JavaDoc f = fields[i];
1199        switch (f.getTypeCode()) {
1200        case 'Z':
1201        case 'B':
1202            f.setOffset(primDataSize++);
1203            break;
1204
1205        case 'C':
1206        case 'S':
1207            f.setOffset(primDataSize);
1208            primDataSize += 2;
1209            break;
1210
1211        case 'I':
1212        case 'F':
1213            f.setOffset(primDataSize);
1214            primDataSize += 4;
1215            break;
1216
1217        case 'J':
1218        case 'D':
1219            f.setOffset(primDataSize);
1220            primDataSize += 8;
1221            break;
1222
1223        case '[':
1224        case 'L':
1225            f.setOffset(numObjFields++);
1226            if (firstObjIndex == -1) {
1227            firstObjIndex = i;
1228            }
1229            break;
1230
1231        default:
1232            throw new InternalError JavaDoc();
1233        }
1234    }
1235    if (firstObjIndex != -1 &&
1236        firstObjIndex + numObjFields != fields.length)
1237    {
1238        throw new InvalidClassException JavaDoc(name, "illegal field order");
1239    }
1240    }
1241    
1242    /**
1243     * If given class is the same as the class associated with this class
1244     * descriptor, returns reference to this class descriptor. Otherwise,
1245     * returns variant of this class descriptor bound to given class.
1246     */

1247    private ObjectStreamClass JavaDoc getVariantFor(Class JavaDoc cl)
1248    throws InvalidClassException JavaDoc
1249    {
1250    if (this.cl == cl) {
1251        return this;
1252    }
1253    ObjectStreamClass JavaDoc desc = new ObjectStreamClass JavaDoc();
1254    if (isProxy) {
1255        desc.initProxy(cl, null, superDesc);
1256    } else {
1257        desc.initNonProxy(this, cl, null, superDesc);
1258    }
1259    return desc;
1260    }
1261
1262    /**
1263     * Returns public no-arg constructor of given class, or null if none found.
1264     * Access checks are disabled on the returned constructor (if any), since
1265     * the defining class may still be non-public.
1266     */

1267    private static Constructor JavaDoc getExternalizableConstructor(Class JavaDoc cl) {
1268    try {
1269        Constructor JavaDoc cons = cl.getDeclaredConstructor(new Class JavaDoc[0]);
1270        cons.setAccessible(true);
1271        return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1272        cons : null;
1273    } catch (NoSuchMethodException JavaDoc ex) {
1274        return null;
1275    }
1276    }
1277
1278    /**
1279     * Returns subclass-accessible no-arg constructor of first non-serializable
1280     * superclass, or null if none found. Access checks are disabled on the
1281     * returned constructor (if any).
1282     */

1283    private static Constructor JavaDoc getSerializableConstructor(Class JavaDoc cl) {
1284    Class JavaDoc initCl = cl;
1285    while (Serializable JavaDoc.class.isAssignableFrom(initCl)) {
1286        if ((initCl = initCl.getSuperclass()) == null) {
1287        return null;
1288        }
1289    }
1290    try {
1291        Constructor JavaDoc cons = initCl.getDeclaredConstructor(new Class JavaDoc[0]);
1292        int mods = cons.getModifiers();
1293        if ((mods & Modifier.PRIVATE) != 0 ||
1294        ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
1295         !packageEquals(cl, initCl)))
1296        {
1297        return null;
1298        }
1299        cons = reflFactory.newConstructorForSerialization(cl, cons);
1300        cons.setAccessible(true);
1301        return cons;
1302    } catch (NoSuchMethodException JavaDoc ex) {
1303        return null;
1304    }
1305    }
1306
1307    /**
1308     * Returns non-static, non-abstract method with given signature provided it
1309     * is defined by or accessible (via inheritance) by the given class, or
1310     * null if no match found. Access checks are disabled on the returned
1311     * method (if any).
1312     */

1313    private static Method JavaDoc getInheritableMethod(Class JavaDoc cl, String JavaDoc name,
1314                           Class JavaDoc[] argTypes,
1315                           Class JavaDoc returnType)
1316    {
1317    Method JavaDoc meth = null;
1318    Class JavaDoc defCl = cl;
1319    while (defCl != null) {
1320        try {
1321        meth = defCl.getDeclaredMethod(name, argTypes);
1322        break;
1323        } catch (NoSuchMethodException JavaDoc ex) {
1324        defCl = defCl.getSuperclass();
1325        }
1326    }
1327
1328    if ((meth == null) || (meth.getReturnType() != returnType)) {
1329        return null;
1330    }
1331    meth.setAccessible(true);
1332    int mods = meth.getModifiers();
1333    if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
1334        return null;
1335    } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
1336        return meth;
1337    } else if ((mods & Modifier.PRIVATE) != 0) {
1338        return (cl == defCl) ? meth : null;
1339    } else {
1340        return packageEquals(cl, defCl) ? meth : null;
1341    }
1342    }
1343
1344    /**
1345     * Returns non-static private method with given signature defined by given
1346     * class, or null if none found. Access checks are disabled on the
1347     * returned method (if any).
1348     */

1349    private static Method JavaDoc getPrivateMethod(Class JavaDoc cl, String JavaDoc name,
1350                       Class JavaDoc[] argTypes,
1351                       Class JavaDoc returnType)
1352    {
1353    try {
1354        Method JavaDoc meth = cl.getDeclaredMethod(name, argTypes);
1355        meth.setAccessible(true);
1356        int mods = meth.getModifiers();
1357        return ((meth.getReturnType() == returnType) &&
1358            ((mods & Modifier.STATIC) == 0) &&
1359            ((mods & Modifier.PRIVATE) != 0)) ? meth : null;
1360    } catch (NoSuchMethodException JavaDoc ex) {
1361        return null;
1362    }
1363    }
1364
1365    /**
1366     * Returns true if classes are defined in the same runtime package, false
1367     * otherwise.
1368     */

1369    private static boolean packageEquals(Class JavaDoc cl1, Class JavaDoc cl2) {
1370    return (cl1.getClassLoader() == cl2.getClassLoader() &&
1371        getPackageName(cl1).equals(getPackageName(cl2)));
1372    }
1373
1374    /**
1375     * Returns package name of given class.
1376     */

1377    private static String JavaDoc getPackageName(Class JavaDoc cl) {
1378    String JavaDoc s = cl.getName();
1379    int i = s.lastIndexOf('[');
1380    if (i >= 0) {
1381        s = s.substring(i + 2);
1382    }
1383    i = s.lastIndexOf('.');
1384    return (i >= 0) ? s.substring(0, i) : "";
1385    }
1386
1387    /**
1388     * Compares class names for equality, ignoring package names. Returns true
1389     * if class names equal, false otherwise.
1390     */

1391    private static boolean classNamesEqual(String JavaDoc name1, String JavaDoc name2) {
1392    name1 = name1.substring(name1.lastIndexOf('.') + 1);
1393    name2 = name2.substring(name2.lastIndexOf('.') + 1);
1394    return name1.equals(name2);
1395    }
1396    
1397    /**
1398     * Returns JVM type signature for given class.
1399     */

1400    static String JavaDoc getClassSignature(Class JavaDoc cl) {
1401    StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
1402    while (cl.isArray()) {
1403        sbuf.append('[');
1404        cl = cl.getComponentType();
1405    }
1406    if (cl.isPrimitive()) {
1407        if (cl == Integer.TYPE) {
1408        sbuf.append('I');
1409        } else if (cl == Byte.TYPE) {
1410        sbuf.append('B');
1411        } else if (cl == Long.TYPE) {
1412        sbuf.append('J');
1413        } else if (cl == Float.TYPE) {
1414        sbuf.append('F');
1415        } else if (cl == Double.TYPE) {
1416        sbuf.append('D');
1417        } else if (cl == Short.TYPE) {
1418        sbuf.append('S');
1419        } else if (cl == Character.TYPE) {
1420        sbuf.append('C');
1421        } else if (cl == Boolean.TYPE) {
1422        sbuf.append('Z');
1423        } else if (cl == Void.TYPE) {
1424        sbuf.append('V');
1425        } else {
1426        throw new InternalError JavaDoc();
1427        }
1428    } else {
1429        sbuf.append('L' + cl.getName().replace('.', '/') + ';');
1430    }
1431    return sbuf.toString();
1432    }
1433
1434    /**
1435     * Returns JVM type signature for given list of parameters and return type.
1436     */

1437    private static String JavaDoc getMethodSignature(Class JavaDoc[] paramTypes,
1438                         Class JavaDoc retType)
1439    {
1440    StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
1441    sbuf.append('(');
1442    for (int i = 0; i < paramTypes.length; i++) {
1443        sbuf.append(getClassSignature(paramTypes[i]));
1444    }
1445    sbuf.append(')');
1446    sbuf.append(getClassSignature(retType));
1447    return sbuf.toString();
1448    }
1449
1450    /**
1451     * Convenience method for throwing an exception that is either a
1452     * RuntimeException, Error, or of some unexpected type (in which case it is
1453     * wrapped inside an IOException).
1454     */

1455    private static void throwMiscException(Throwable JavaDoc th) throws IOException JavaDoc {
1456    if (th instanceof RuntimeException JavaDoc) {
1457        throw (RuntimeException JavaDoc) th;
1458    } else if (th instanceof Error JavaDoc) {
1459        throw (Error JavaDoc) th;
1460    } else {
1461        IOException JavaDoc ex = new IOException JavaDoc("unexpected exception type");
1462        ex.initCause(th);
1463        throw ex;
1464    }
1465    }
1466
1467    /**
1468     * Returns ObjectStreamField array describing the serializable fields of
1469     * the given class. Serializable fields backed by an actual field of the
1470     * class are represented by ObjectStreamFields with corresponding non-null
1471     * Field objects. Throws InvalidClassException if the (explicitly
1472     * declared) serializable fields are invalid.
1473     */

1474    private static ObjectStreamField JavaDoc[] getSerialFields(Class JavaDoc cl)
1475    throws InvalidClassException JavaDoc
1476    {
1477    ObjectStreamField JavaDoc[] fields;
1478    if (Serializable JavaDoc.class.isAssignableFrom(cl) &&
1479        !Externalizable JavaDoc.class.isAssignableFrom(cl) &&
1480        !Proxy.isProxyClass(cl) &&
1481        !cl.isInterface())
1482    {
1483        if ((fields = getDeclaredSerialFields(cl)) == null) {
1484        fields = getDefaultSerialFields(cl);
1485        }
1486        Arrays.sort(fields);
1487    } else {
1488        fields = NO_FIELDS;
1489    }
1490    return fields;
1491    }
1492    
1493    /**
1494     * Returns serializable fields of given class as defined explicitly by a
1495     * "serialPersistentFields" field, or null if no appropriate
1496     * "serialPersistentFields" field is defined. Serializable fields backed
1497     * by an actual field of the class are represented by ObjectStreamFields
1498     * with corresponding non-null Field objects. For compatibility with past
1499     * releases, a "serialPersistentFields" field with a null value is
1500     * considered equivalent to not declaring "serialPersistentFields". Throws
1501     * InvalidClassException if the declared serializable fields are
1502     * invalid--e.g., if multiple fields share the same name.
1503     */

1504    private static ObjectStreamField JavaDoc[] getDeclaredSerialFields(Class JavaDoc cl)
1505    throws InvalidClassException JavaDoc
1506    {
1507    ObjectStreamField JavaDoc[] serialPersistentFields = null;
1508    try {
1509        Field JavaDoc f = cl.getDeclaredField("serialPersistentFields");
1510        int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
1511        if ((f.getModifiers() & mask) == mask) {
1512        f.setAccessible(true);
1513        serialPersistentFields = (ObjectStreamField JavaDoc[]) f.get(null);
1514        }
1515    } catch (Exception JavaDoc ex) {
1516    }
1517    if (serialPersistentFields == null) {
1518        return null;
1519    } else if (serialPersistentFields.length == 0) {
1520        return NO_FIELDS;
1521    }
1522    
1523    ObjectStreamField JavaDoc[] boundFields =
1524        new ObjectStreamField JavaDoc[serialPersistentFields.length];
1525    Set JavaDoc fieldNames = new HashSet JavaDoc(serialPersistentFields.length);
1526
1527    for (int i = 0; i < serialPersistentFields.length; i++) {
1528        ObjectStreamField JavaDoc spf = serialPersistentFields[i];
1529
1530        String JavaDoc fname = spf.getName();
1531        if (fieldNames.contains(fname)) {
1532        throw new InvalidClassException JavaDoc(
1533            "multiple serializable fields named " + fname);
1534        }
1535        fieldNames.add(fname);
1536
1537        try {
1538        Field JavaDoc f = cl.getDeclaredField(fname);
1539        if ((f.getType() == spf.getType()) &&
1540            ((f.getModifiers() & Modifier.STATIC) == 0))
1541        {
1542            boundFields[i] =
1543            new ObjectStreamField JavaDoc(f, spf.isUnshared(), true);
1544        }
1545        } catch (NoSuchFieldException JavaDoc ex) {
1546        }
1547        if (boundFields[i] == null) {
1548        boundFields[i] = new ObjectStreamField JavaDoc(
1549            fname, spf.getType(), spf.isUnshared());
1550        }
1551    }
1552    return boundFields;
1553    }
1554
1555    /**
1556     * Returns array of ObjectStreamFields corresponding to all non-static
1557     * non-transient fields declared by given class. Each ObjectStreamField
1558     * contains a Field object for the field it represents. If no default
1559     * serializable fields exist, NO_FIELDS is returned.
1560     */

1561    private static ObjectStreamField JavaDoc[] getDefaultSerialFields(Class JavaDoc cl) {
1562    Field JavaDoc[] clFields = cl.getDeclaredFields();
1563    ArrayList JavaDoc list = new ArrayList JavaDoc();
1564    int mask = Modifier.STATIC | Modifier.TRANSIENT;
1565
1566    for (int i = 0; i < clFields.length; i++) {
1567        if ((clFields[i].getModifiers() & mask) == 0) {
1568        list.add(new ObjectStreamField JavaDoc(clFields[i], false, true));
1569        }
1570    }
1571    int size = list.size();
1572    return (size == 0) ? NO_FIELDS :
1573        (ObjectStreamField JavaDoc[]) list.toArray(new ObjectStreamField JavaDoc[size]);
1574    }
1575
1576    /**
1577     * Returns explicit serial version UID value declared by given class, or
1578     * null if none.
1579     */

1580    private static Long JavaDoc getDeclaredSUID(Class JavaDoc cl) {
1581    try {
1582        Field JavaDoc f = cl.getDeclaredField("serialVersionUID");
1583        int mask = Modifier.STATIC | Modifier.FINAL;
1584        if ((f.getModifiers() & mask) == mask) {
1585        f.setAccessible(true);
1586        return new Long JavaDoc(f.getLong(null));
1587        }
1588    } catch (Exception JavaDoc ex) {
1589    }
1590    return null;
1591    }
1592
1593    /**
1594     * Computes the default serial version UID value for the given class.
1595     */

1596    private static long computeDefaultSUID(Class JavaDoc cl) {
1597    if (!Serializable JavaDoc.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
1598    {
1599        return 0L;
1600    }
1601
1602    try {
1603        ByteArrayOutputStream JavaDoc bout = new ByteArrayOutputStream JavaDoc();
1604        DataOutputStream JavaDoc dout = new DataOutputStream JavaDoc(bout);
1605
1606        dout.writeUTF(cl.getName());
1607        
1608        int classMods = cl.getModifiers() &
1609        (Modifier.PUBLIC | Modifier.FINAL |
1610         Modifier.INTERFACE | Modifier.ABSTRACT);
1611
1612        /*
1613         * compensate for javac bug in which ABSTRACT bit was set for an
1614         * interface only if the interface declared methods
1615         */

1616        Method JavaDoc[] methods = cl.getDeclaredMethods();
1617        if ((classMods & Modifier.INTERFACE) != 0) {
1618        classMods = (methods.length > 0) ?
1619            (classMods | Modifier.ABSTRACT) :
1620            (classMods & ~Modifier.ABSTRACT);
1621        }
1622        dout.writeInt(classMods);
1623        
1624        if (!cl.isArray()) {
1625        /*
1626         * compensate for change in 1.2FCS in which
1627         * Class.getInterfaces() was modified to return Cloneable and
1628         * Serializable for array classes.
1629         */

1630        Class JavaDoc[] interfaces = cl.getInterfaces();
1631        String JavaDoc[] ifaceNames = new String JavaDoc[interfaces.length];
1632        for (int i = 0; i < interfaces.length; i++) {
1633            ifaceNames[i] = interfaces[i].getName();
1634        }
1635        Arrays.sort(ifaceNames);
1636        for (int i = 0; i < ifaceNames.length; i++) {
1637            dout.writeUTF(ifaceNames[i]);
1638        }
1639        }
1640        
1641        Field JavaDoc[] fields = cl.getDeclaredFields();
1642        MemberSignature[] fieldSigs = new MemberSignature[fields.length];
1643        for (int i = 0; i < fields.length; i++) {
1644        fieldSigs[i] = new MemberSignature(fields[i]);
1645        }
1646        Arrays.sort(fieldSigs, new Comparator JavaDoc() {
1647        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1648            String JavaDoc name1 = ((MemberSignature) o1).name;
1649            String JavaDoc name2 = ((MemberSignature) o2).name;
1650            return name1.compareTo(name2);
1651        }
1652        });
1653        for (int i = 0; i < fieldSigs.length; i++) {
1654        MemberSignature sig = fieldSigs[i];
1655        int mods = sig.member.getModifiers() &
1656            (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1657             Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
1658             Modifier.TRANSIENT);
1659        if (((mods & Modifier.PRIVATE) == 0) ||
1660            ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
1661        {
1662            dout.writeUTF(sig.name);
1663            dout.writeInt(mods);
1664            dout.writeUTF(sig.signature);
1665        }
1666        }
1667        
1668        if (hasStaticInitializer(cl)) {
1669        dout.writeUTF("<clinit>");
1670        dout.writeInt(Modifier.STATIC);
1671        dout.writeUTF("()V");
1672        }
1673
1674        Constructor JavaDoc[] cons = cl.getDeclaredConstructors();
1675        MemberSignature[] consSigs = new MemberSignature[cons.length];
1676        for (int i = 0; i < cons.length; i++) {
1677        consSigs[i] = new MemberSignature(cons[i]);
1678        }
1679        Arrays.sort(consSigs, new Comparator JavaDoc() {
1680        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1681            String JavaDoc sig1 = ((MemberSignature) o1).signature;
1682            String JavaDoc sig2 = ((MemberSignature) o2).signature;
1683            return sig1.compareTo(sig2);
1684        }
1685        });
1686        for (int i = 0; i < consSigs.length; i++) {
1687        MemberSignature sig = consSigs[i];
1688        int mods = sig.member.getModifiers() &
1689            (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1690             Modifier.STATIC | Modifier.FINAL |
1691             Modifier.SYNCHRONIZED | Modifier.NATIVE |
1692             Modifier.ABSTRACT | Modifier.STRICT);
1693        if ((mods & Modifier.PRIVATE) == 0) {
1694            dout.writeUTF("<init>");
1695            dout.writeInt(mods);
1696            dout.writeUTF(sig.signature.replace('/', '.'));
1697        }
1698        }
1699        
1700        MemberSignature[] methSigs = new MemberSignature[methods.length];
1701        for (int i = 0; i < methods.length; i++) {
1702        methSigs[i] = new MemberSignature(methods[i]);
1703        }
1704        Arrays.sort(methSigs, new Comparator JavaDoc() {
1705        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1706            MemberSignature ms1 = (MemberSignature) o1;
1707            MemberSignature ms2 = (MemberSignature) o2;
1708            int comp = ms1.name.compareTo(ms2.name);
1709            if (comp == 0) {
1710            comp = ms1.signature.compareTo(ms2.signature);
1711            }
1712            return comp;
1713        }
1714        });
1715        for (int i = 0; i < methSigs.length; i++) {
1716        MemberSignature sig = methSigs[i];
1717        int mods = sig.member.getModifiers() &
1718            (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1719             Modifier.STATIC | Modifier.FINAL |
1720             Modifier.SYNCHRONIZED | Modifier.NATIVE |
1721             Modifier.ABSTRACT | Modifier.STRICT);
1722        if ((mods & Modifier.PRIVATE) == 0) {
1723            dout.writeUTF(sig.name);
1724            dout.writeInt(mods);
1725            dout.writeUTF(sig.signature.replace('/', '.'));
1726        }
1727        }
1728
1729        dout.flush();
1730
1731        MessageDigest JavaDoc md = MessageDigest.getInstance("SHA");
1732        byte[] hashBytes = md.digest(bout.toByteArray());
1733        long hash = 0;
1734        for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
1735        hash = (hash << 8) | (hashBytes[i] & 0xFF);
1736        }
1737        return hash;
1738    } catch (IOException JavaDoc ex) {
1739        throw new InternalError JavaDoc();
1740    } catch (NoSuchAlgorithmException JavaDoc ex) {
1741        throw new SecurityException JavaDoc(ex.getMessage());
1742    }
1743    }
1744
1745    /**
1746     * Returns true if the given class defines a static initializer method,
1747     * false otherwise.
1748     */

1749    private native static boolean hasStaticInitializer(Class JavaDoc cl);
1750
1751    /**
1752     * Class for computing and caching field/constructor/method signatures
1753     * during serialVersionUID calculation.
1754     */

1755    private static class MemberSignature {
1756
1757    public final Member JavaDoc member;
1758    public final String JavaDoc name;
1759    public final String JavaDoc signature;
1760    
1761    public MemberSignature(Field JavaDoc field) {
1762        member = field;
1763        name = field.getName();
1764        signature = getClassSignature(field.getType());
1765    }
1766    
1767    public MemberSignature(Constructor JavaDoc cons) {
1768        member = cons;
1769        name = cons.getName();
1770        signature = getMethodSignature(
1771        cons.getParameterTypes(), Void.TYPE);
1772    }
1773    
1774    public MemberSignature(Method JavaDoc meth) {
1775        member = meth;
1776        name = meth.getName();
1777        signature = getMethodSignature(
1778        meth.getParameterTypes(), meth.getReturnType());
1779    }
1780    }
1781    
1782    /**
1783     * Class for setting and retrieving serializable field values in batch.
1784     */

1785    // REMIND: dynamically generate these?
1786
private static class FieldReflector {
1787    
1788    /** handle for performing unsafe operations */
1789    private static final Unsafe unsafe = Unsafe.getUnsafe();
1790
1791    /** fields to operate on */
1792    private final ObjectStreamField JavaDoc[] fields;
1793    /** number of primitive fields */
1794    private final int numPrimFields;
1795    /** unsafe field keys */
1796    private final long[] keys;
1797    /** field data offsets */
1798    private final int[] offsets;
1799    /** field type codes */
1800    private final char[] typeCodes;
1801    /** field types */
1802    private final Class JavaDoc[] types;
1803
1804    /**
1805     * Constructs FieldReflector capable of setting/getting values from the
1806     * subset of fields whose ObjectStreamFields contain non-null
1807     * reflective Field objects. ObjectStreamFields with null Fields are
1808     * treated as filler, for which get operations return default values
1809     * and set operations discard given values.
1810     */

1811    FieldReflector(ObjectStreamField JavaDoc[] fields) {
1812        this.fields = fields;
1813        int nfields = fields.length;
1814        keys = new long[nfields];
1815        offsets = new int[nfields];
1816        typeCodes = new char[nfields];
1817        ArrayList JavaDoc typeList = new ArrayList JavaDoc();
1818        
1819        for (int i = 0; i < nfields; i++) {
1820        ObjectStreamField JavaDoc f = fields[i];
1821        Field JavaDoc rf = f.getField();
1822        keys[i] = (rf != null) ?
1823            unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
1824        offsets[i] = f.getOffset();
1825        typeCodes[i] = f.getTypeCode();
1826        if (!f.isPrimitive()) {
1827            typeList.add((rf != null) ? rf.getType() : null);
1828        }
1829        }
1830        
1831        types = (Class JavaDoc[]) typeList.toArray(new Class JavaDoc[typeList.size()]);
1832        numPrimFields = nfields - types.length;
1833    }
1834
1835    /**
1836     * Returns list of ObjectStreamFields representing fields operated on
1837     * by this reflector. The shared/unshared values and Field objects
1838     * contained by ObjectStreamFields in the list reflect their bindings
1839     * to locally defined serializable fields.
1840     */

1841    ObjectStreamField JavaDoc[] getFields() {
1842        return fields;
1843    }
1844
1845    /**
1846     * Fetches the serializable primitive field values of object obj and
1847     * marshals them into byte array buf starting at offset 0. The caller
1848     * is responsible for ensuring that obj is of the proper type.
1849     */

1850    void getPrimFieldValues(Object JavaDoc obj, byte[] buf) {
1851        if (obj == null) {
1852        throw new NullPointerException JavaDoc();
1853        }
1854        /* assuming checkDefaultSerialize() has been called on the class
1855         * descriptor this FieldReflector was obtained from, no field keys
1856         * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
1857         */

1858        for (int i = 0; i < numPrimFields; i++) {
1859        long key = keys[i];
1860        int off = offsets[i];
1861        switch (typeCodes[i]) {
1862            case 'Z':
1863            Bits.putBoolean(buf, off, unsafe.getBoolean(obj, key));
1864            break;
1865
1866            case 'B':
1867            buf[off] = unsafe.getByte(obj, key);
1868            break;
1869
1870            case 'C':
1871            Bits.putChar(buf, off, unsafe.getChar(obj, key));
1872            break;
1873
1874            case 'S':
1875            Bits.putShort(buf, off, unsafe.getShort(obj, key));
1876            break;
1877
1878            case 'I':
1879            Bits.putInt(buf, off, unsafe.getInt(obj, key));
1880            break;
1881
1882            case 'F':
1883            Bits.putFloat(buf, off, unsafe.getFloat(obj, key));
1884            break;
1885
1886            case 'J':
1887            Bits.putLong(buf, off, unsafe.getLong(obj, key));
1888            break;
1889
1890            case 'D':
1891            Bits.putDouble(buf, off, unsafe.getDouble(obj, key));
1892            break;
1893
1894            default:
1895            throw new InternalError JavaDoc();
1896        }
1897        }
1898    }
1899
1900    /**
1901     * Sets the serializable primitive fields of object obj using values
1902     * unmarshalled from byte array buf starting at offset 0. The caller
1903     * is responsible for ensuring that obj is of the proper type.
1904     */

1905    void setPrimFieldValues(Object JavaDoc obj, byte[] buf) {
1906        if (obj == null) {
1907        throw new NullPointerException JavaDoc();
1908        }
1909        for (int i = 0; i < numPrimFields; i++) {
1910        long key = keys[i];
1911        if (key == Unsafe.INVALID_FIELD_OFFSET) {
1912            continue; // discard value
1913
}
1914        int off = offsets[i];
1915        switch (typeCodes[i]) {
1916            case 'Z':
1917            unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off));
1918            break;
1919
1920            case 'B':
1921            unsafe.putByte(obj, key, buf[off]);
1922            break;
1923
1924            case 'C':
1925            unsafe.putChar(obj, key, Bits.getChar(buf, off));
1926            break;
1927
1928            case 'S':
1929            unsafe.putShort(obj, key, Bits.getShort(buf, off));
1930            break;
1931
1932            case 'I':
1933            unsafe.putInt(obj, key, Bits.getInt(buf, off));
1934            break;
1935
1936            case 'F':
1937            unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
1938            break;
1939
1940            case 'J':
1941            unsafe.putLong(obj, key, Bits.getLong(buf, off));
1942            break;
1943
1944            case 'D':
1945            unsafe.putDouble(obj, key, Bits.getDouble(buf, off));
1946            break;
1947
1948            default:
1949            throw new InternalError JavaDoc();
1950        }
1951        }
1952    }
1953
1954    /**
1955     * Fetches the serializable object field values of object obj and
1956     * stores them in array vals starting at offset 0. The caller is
1957     * responsible for ensuring that obj is of the proper type.
1958     */

1959    void getObjFieldValues(Object JavaDoc obj, Object JavaDoc[] vals) {
1960        if (obj == null) {
1961        throw new NullPointerException JavaDoc();
1962        }
1963        /* assuming checkDefaultSerialize() has been called on the class
1964         * descriptor this FieldReflector was obtained from, no field keys
1965         * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
1966         */

1967        for (int i = numPrimFields; i < fields.length; i++) {
1968        switch (typeCodes[i]) {
1969            case 'L':
1970            case '[':
1971            vals[offsets[i]] = unsafe.getObject(obj, keys[i]);
1972            break;
1973            
1974            default:
1975            throw new InternalError JavaDoc();
1976        }
1977        }
1978    }
1979
1980    /**
1981     * Sets the serializable object fields of object obj using values from
1982     * array vals starting at offset 0. The caller is responsible for
1983     * ensuring that obj is of the proper type; however, attempts to set a
1984     * field with a value of the wrong type will trigger an appropriate
1985     * ClassCastException.
1986     */

1987    void setObjFieldValues(Object JavaDoc obj, Object JavaDoc[] vals) {
1988        if (obj == null) {
1989        throw new NullPointerException JavaDoc();
1990        }
1991        for (int i = numPrimFields; i < fields.length; i++) {
1992        long key = keys[i];
1993        if (key == Unsafe.INVALID_FIELD_OFFSET) {
1994            continue; // discard value
1995
}
1996        switch (typeCodes[i]) {
1997            case 'L':
1998            case '[':
1999            Object JavaDoc val = vals[offsets[i]];
2000            if (val != null &&
2001                !types[i - numPrimFields].isInstance(val))
2002            {
2003                Field JavaDoc f = fields[i].getField();
2004                throw new ClassCastException JavaDoc(
2005                "cannot assign instance of " +
2006                val.getClass().getName() + " to field " +
2007                f.getDeclaringClass().getName() + "." +
2008                f.getName() + " of type " +
2009                f.getType().getName() + " in instance of " +
2010                obj.getClass().getName());
2011            }
2012            unsafe.putObject(obj, key, val);
2013            break;
2014            
2015            default:
2016            throw new InternalError JavaDoc();
2017        }
2018        }
2019    }
2020    }
2021    
2022    /**
2023     * Matches given set of serializable fields with serializable fields
2024     * described by the given local class descriptor, and returns a
2025     * FieldReflector instance capable of setting/getting values from the
2026     * subset of fields that match (non-matching fields are treated as filler,
2027     * for which get operations return default values and set operations
2028     * discard given values). Throws InvalidClassException if unresolvable
2029     * type conflicts exist between the two sets of fields.
2030     */

2031    private static FieldReflector getReflector(ObjectStreamField JavaDoc[] fields,
2032                           ObjectStreamClass JavaDoc localDesc)
2033    throws InvalidClassException JavaDoc
2034    {
2035    // class irrelevant if no fields
2036
Class JavaDoc cl = (localDesc != null && fields.length > 0) ?
2037        localDesc.cl : null;
2038    processQueue(Caches.reflectorsQueue, Caches.reflectors);
2039    FieldReflectorKey key = new FieldReflectorKey(cl, fields,
2040                              Caches.reflectorsQueue);
2041    Reference JavaDoc<?> ref = Caches.reflectors.get(key);
2042    Object JavaDoc entry = null;
2043    if (ref != null) {
2044        entry = ref.get();
2045    }
2046    EntryFuture future = null;
2047    if (entry == null) {
2048        EntryFuture newEntry = new EntryFuture();
2049        Reference JavaDoc<?> newRef = new SoftReference JavaDoc<EntryFuture>(newEntry);
2050        do {
2051        if (ref != null) {
2052            Caches.reflectors.remove(key, ref);
2053        }
2054        ref = Caches.reflectors.putIfAbsent(key, newRef);
2055        if (ref != null) {
2056            entry = ref.get();
2057        }
2058        } while (ref != null && entry == null);
2059        if (entry == null) {
2060        future = newEntry;
2061        }
2062    }
2063    
2064    if (entry instanceof FieldReflector) { // check common case first
2065
return (FieldReflector) entry;
2066    } else if (entry instanceof EntryFuture) {
2067        entry = ((EntryFuture) entry).get();
2068    } else if (entry == null) {
2069        try {
2070        entry = new FieldReflector(matchFields(fields, localDesc));
2071        } catch (Throwable JavaDoc th) {
2072        entry = th;
2073        }
2074        future.set(entry);
2075        Caches.reflectors.put(key, new SoftReference JavaDoc<Object JavaDoc>(entry));
2076    }
2077    
2078    if (entry instanceof FieldReflector) {
2079        return (FieldReflector) entry;
2080    } else if (entry instanceof InvalidClassException JavaDoc) {
2081        throw (InvalidClassException JavaDoc) entry;
2082    } else if (entry instanceof RuntimeException JavaDoc) {
2083        throw (RuntimeException JavaDoc) entry;
2084    } else if (entry instanceof Error JavaDoc) {
2085        throw (Error JavaDoc) entry;
2086    } else {
2087        throw new InternalError JavaDoc("unexpected entry: " + entry);
2088    }
2089    }
2090    
2091    /**
2092     * FieldReflector cache lookup key. Keys are considered equal if they
2093     * refer to the same class and equivalent field formats.
2094     */

2095    private static class FieldReflectorKey extends WeakReference JavaDoc<Class JavaDoc<?>> {
2096    
2097    private final String JavaDoc sigs;
2098    private final int hash;
2099    private final boolean nullClass;
2100    
2101    FieldReflectorKey(Class JavaDoc<?> cl, ObjectStreamField JavaDoc[] fields,
2102              ReferenceQueue JavaDoc<Class JavaDoc<?>> queue)
2103    {
2104        super(cl, queue);
2105        nullClass = (cl == null);
2106        StringBuilder JavaDoc sbuf = new StringBuilder JavaDoc();
2107        for (int i = 0; i < fields.length; i++) {
2108        ObjectStreamField JavaDoc f = fields[i];
2109        sbuf.append(f.getName()).append(f.getSignature());
2110        }
2111        sigs = sbuf.toString();
2112        hash = System.identityHashCode(cl) + sigs.hashCode();
2113    }
2114    
2115    public int hashCode() {
2116        return hash;
2117    }
2118    
2119    public boolean equals(Object JavaDoc obj) {
2120        if (obj == this) {
2121        return true;
2122        }
2123
2124        if (obj instanceof FieldReflectorKey) {
2125        FieldReflectorKey other = (FieldReflectorKey) obj;
2126        Class JavaDoc<?> referent;
2127        return (nullClass ? other.nullClass
2128                      : ((referent = get()) != null) &&
2129                        (referent == other.get())) &&
2130            sigs.equals(other.sigs);
2131        } else {
2132        return false;
2133        }
2134    }
2135    }
2136    
2137    /**
2138     * Matches given set of serializable fields with serializable fields
2139     * obtained from the given local class descriptor (which contain bindings
2140     * to reflective Field objects). Returns list of ObjectStreamFields in
2141     * which each ObjectStreamField whose signature matches that of a local
2142     * field contains a Field object for that field; unmatched
2143     * ObjectStreamFields contain null Field objects. Shared/unshared settings
2144     * of the returned ObjectStreamFields also reflect those of matched local
2145     * ObjectStreamFields. Throws InvalidClassException if unresolvable type
2146     * conflicts exist between the two sets of fields.
2147     */

2148    private static ObjectStreamField JavaDoc[] matchFields(ObjectStreamField JavaDoc[] fields,
2149                           ObjectStreamClass JavaDoc localDesc)
2150    throws InvalidClassException JavaDoc
2151    {
2152    ObjectStreamField JavaDoc[] localFields = (localDesc != null) ?
2153        localDesc.fields : NO_FIELDS;
2154    
2155    /*
2156     * Even if fields == localFields, we cannot simply return localFields
2157     * here. In previous implementations of serialization,
2158     * ObjectStreamField.getType() returned Object.class if the
2159     * ObjectStreamField represented a non-primitive field and belonged to
2160     * a non-local class descriptor. To preserve this (questionable)
2161     * behavior, the ObjectStreamField instances returned by matchFields
2162     * cannot report non-primitive types other than Object.class; hence
2163     * localFields cannot be returned directly.
2164     */

2165    
2166    ObjectStreamField JavaDoc[] matches = new ObjectStreamField JavaDoc[fields.length];
2167    for (int i = 0; i < fields.length; i++) {
2168        ObjectStreamField JavaDoc f = fields[i], m = null;
2169        for (int j = 0; j < localFields.length; j++) {
2170        ObjectStreamField JavaDoc lf = localFields[j];
2171        if (f.getName().equals(lf.getName())) {
2172            if ((f.isPrimitive() || lf.isPrimitive()) &&
2173            f.getTypeCode() != lf.getTypeCode())
2174            {
2175            throw new InvalidClassException JavaDoc(localDesc.name,
2176                "incompatible types for field " + f.getName());
2177            }
2178            if (lf.getField() != null) {
2179            m = new ObjectStreamField JavaDoc(
2180                lf.getField(), lf.isUnshared(), false);
2181            } else {
2182            m = new ObjectStreamField JavaDoc(
2183                lf.getName(), lf.getSignature(), lf.isUnshared());
2184            }
2185        }
2186        }
2187        if (m == null) {
2188        m = new ObjectStreamField JavaDoc(
2189            f.getName(), f.getSignature(), false);
2190        }
2191        m.setOffset(f.getOffset());
2192        matches[i] = m;
2193    }
2194    return matches;
2195    }
2196
2197    /**
2198     * Removes from the specified map any keys that have been enqueued
2199     * on the specified reference queue.
2200     */

2201    static void processQueue(ReferenceQueue JavaDoc<Class JavaDoc<?>> queue,
2202                 ConcurrentMap JavaDoc<? extends
2203                 WeakReference JavaDoc<Class JavaDoc<?>>, ?> map)
2204    {
2205    Reference JavaDoc<? extends Class JavaDoc<?>> ref;
2206    while((ref = queue.poll()) != null) {
2207        map.remove(ref);
2208    }
2209    }
2210
2211    /**
2212     * Weak key for Class objects.
2213     *
2214     **/

2215    static class WeakClassKey extends WeakReference JavaDoc<Class JavaDoc<?>> {
2216    /**
2217     * saved value of the referent's identity hash code, to maintain
2218     * a consistent hash code after the referent has been cleared
2219     */

2220    private final int hash;
2221
2222    /**
2223     * Create a new WeakClassKey to the given object, registered
2224     * with a queue.
2225     */

2226    WeakClassKey(Class JavaDoc<?> cl, ReferenceQueue JavaDoc<Class JavaDoc<?>> refQueue) {
2227        super(cl, refQueue);
2228        hash = System.identityHashCode(cl);
2229    }
2230
2231    /**
2232     * Returns the identity hash code of the original referent.
2233     */

2234    public int hashCode() {
2235        return hash;
2236    }
2237
2238    /**
2239     * Returns true if the given object is this identical
2240     * WeakClassKey instance, or, if this object's referent has not
2241     * been cleared, if the given object is another WeakClassKey
2242     * instance with the identical non-null referent as this one.
2243     */

2244    public boolean equals(Object JavaDoc obj) {
2245        if (obj == this) {
2246        return true;
2247        }
2248
2249        if (obj instanceof WeakClassKey) {
2250        Object JavaDoc referent = get();
2251        return (referent != null) &&
2252               (referent == ((WeakClassKey) obj).get());
2253        } else {
2254        return false;
2255        }
2256    }
2257    }
2258}
2259
Popular Tags