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 hasWriteOb