KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > io > ObjectOutputStream


1 /*
2  * @(#)ObjectOutputStream.java 1.147 06/07/26
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.io.ObjectStreamClass.WeakClassKey JavaDoc;
11 import java.lang.ref.ReferenceQueue JavaDoc;
12 import java.security.AccessController JavaDoc;
13 import java.security.PrivilegedAction JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.concurrent.ConcurrentHashMap JavaDoc;
16 import java.util.concurrent.ConcurrentMap JavaDoc;
17 import static java.io.ObjectStreamClass.processQueue JavaDoc;
18
19 /**
20  * An ObjectOutputStream writes primitive data types and graphs of Java objects
21  * to an OutputStream. The objects can be read (reconstituted) using an
22  * ObjectInputStream. Persistent storage of objects can be accomplished by
23  * using a file for the stream. If the stream is a network socket stream, the
24  * objects can be reconstituted on another host or in another process.
25  *
26  * <p>Only objects that support the java.io.Serializable interface can be
27  * written to streams. The class of each serializable object is encoded
28  * including the class name and signature of the class, the values of the
29  * object's fields and arrays, and the closure of any other objects referenced
30  * from the initial objects.
31  *
32  * <p>The method writeObject is used to write an object to the stream. Any
33  * object, including Strings and arrays, is written with writeObject. Multiple
34  * objects or primitives can be written to the stream. The objects must be
35  * read back from the corresponding ObjectInputstream with the same types and
36  * in the same order as they were written.
37  *
38  * <p>Primitive data types can also be written to the stream using the
39  * appropriate methods from DataOutput. Strings can also be written using the
40  * writeUTF method.
41  *
42  * <p>The default serialization mechanism for an object writes the class of the
43  * object, the class signature, and the values of all non-transient and
44  * non-static fields. References to other objects (except in transient or
45  * static fields) cause those objects to be written also. Multiple references
46  * to a single object are encoded using a reference sharing mechanism so that
47  * graphs of objects can be restored to the same shape as when the original was
48  * written.
49  *
50  * <p>For example to write an object that can be read by the example in
51  * ObjectInputStream:
52  * <br>
53  * <pre>
54  * FileOutputStream fos = new FileOutputStream("t.tmp");
55  * ObjectOutputStream oos = new ObjectOutputStream(fos);
56  *
57  * oos.writeInt(12345);
58  * oos.writeObject("Today");
59  * oos.writeObject(new Date());
60  *
61  * oos.close();
62  * </pre>
63  *
64  * <p>Classes that require special handling during the serialization and
65  * deserialization process must implement special methods with these exact
66  * signatures:
67  * <br>
68  * <pre>
69  * private void readObject(java.io.ObjectInputStream stream)
70  * throws IOException, ClassNotFoundException;
71  * private void writeObject(java.io.ObjectOutputStream stream)
72  * throws IOException
73  * </pre>
74  *
75  * <p>The writeObject method is responsible for writing the state of the object
76  * for its particular class so that the corresponding readObject method can
77  * restore it. The method does not need to concern itself with the state
78  * belonging to the object's superclasses or subclasses. State is saved by
79  * writing the individual fields to the ObjectOutputStream using the
80  * writeObject method or by using the methods for primitive data types
81  * supported by DataOutput.
82  *
83  * <p>Serialization does not write out the fields of any object that does not
84  * implement the java.io.Serializable interface. Subclasses of Objects that
85  * are not serializable can be serializable. In this case the non-serializable
86  * class must have a no-arg constructor to allow its fields to be initialized.
87  * In this case it is the responsibility of the subclass to save and restore
88  * the state of the non-serializable class. It is frequently the case that the
89  * fields of that class are accessible (public, package, or protected) or that
90  * there are get and set methods that can be used to restore the state.
91  *
92  * <p>Serialization of an object can be prevented by implementing writeObject
93  * and readObject methods that throw the NotSerializableException. The
94  * exception will be caught by the ObjectOutputStream and abort the
95  * serialization process.
96  *
97  * <p>Implementing the Externalizable interface allows the object to assume
98  * complete control over the contents and format of the object's serialized
99  * form. The methods of the Externalizable interface, writeExternal and
100  * readExternal, are called to save and restore the objects state. When
101  * implemented by a class they can write and read their own state using all of
102  * the methods of ObjectOutput and ObjectInput. It is the responsibility of
103  * the objects to handle any versioning that occurs.
104  *
105  * <p>Enum constants are serialized differently than ordinary serializable or
106  * externalizable objects. The serialized form of an enum constant consists
107  * solely of its name; field values of the constant are not transmitted. To
108  * serialize an enum constant, ObjectOutputStream writes the string returned by
109  * the constant's name method. Like other serializable or externalizable
110  * objects, enum constants can function as the targets of back references
111  * appearing subsequently in the serialization stream. The process by which
112  * enum constants are serialized cannot be customized; any class-specific
113  * writeObject and writeReplace methods defined by enum types are ignored
114  * during serialization. Similarly, any serialPersistentFields or
115  * serialVersionUID field declarations are also ignored--all enum types have a
116  * fixed serialVersionUID of 0L.
117  *
118  * <p>Primitive data, excluding serializable fields and externalizable data, is
119  * written to the ObjectOutputStream in block-data records. A block data record
120  * is composed of a header and data. The block data header consists of a marker
121  * and the number of bytes to follow the header. Consecutive primitive data
122  * writes are merged into one block-data record. The blocking factor used for
123  * a block-data record will be 1024 bytes. Each block-data record will be
124  * filled up to 1024 bytes, or be written whenever there is a termination of
125  * block-data mode. Calls to the ObjectOutputStream methods writeObject,
126  * defaultWriteObject and writeFields initially terminate any existing
127  * block-data record.
128  *
129  * @author Mike Warres
130  * @author Roger Riggs
131  * @version 1.147, 06/07/26
132  * @see java.io.DataOutput
133  * @see java.io.ObjectInputStream
134  * @see java.io.Serializable
135  * @see java.io.Externalizable
136  * @see <a HREF="../../../guide/serialization/spec/output.doc.html">Object Serialization Specification, Section 2, Object Output Classes</a>
137  * @since JDK1.1
138  */

139 public class ObjectOutputStream
140     extends OutputStream JavaDoc implements ObjectOutput JavaDoc, ObjectStreamConstants JavaDoc
141 {
142     private static class Caches {
143     /** cache of subclass security audit results */
144     static final ConcurrentMap JavaDoc<WeakClassKey,Boolean JavaDoc> subclassAudits =
145         new ConcurrentHashMap JavaDoc<WeakClassKey,Boolean JavaDoc>();
146
147     /** queue for WeakReferences to audited subclasses */
148     static final ReferenceQueue JavaDoc<Class JavaDoc<?>> subclassAuditsQueue =
149         new ReferenceQueue JavaDoc<Class JavaDoc<?>>();
150     }
151
152     /** filter stream for handling block data conversion */
153     private final BlockDataOutputStream bout;
154     /** obj -> wire handle map */
155     private final HandleTable handles;
156     /** obj -> replacement obj map */
157     private final ReplaceTable subs;
158     /** stream protocol version */
159     private int protocol = PROTOCOL_VERSION_2;
160     /** recursion depth */
161     private int depth;
162
163     /** buffer for writing primitive field values */
164     private byte[] primVals;
165
166     /** if true, invoke writeObjectOverride() instead of writeObject() */
167     private final boolean enableOverride;
168     /** if true, invoke replaceObject() */
169     private boolean enableReplace;
170
171     // values below valid only during upcalls to writeObject()/writeExternal()
172
/** object currently being serialized */
173     private Object JavaDoc curObj;
174     /** descriptor for current class (null if in writeExternal()) */
175     private ObjectStreamClass JavaDoc curDesc;
176     /** current PutField object */
177     private PutFieldImpl curPut;
178
179     /**
180      * Creates an ObjectOutputStream that writes to the specified OutputStream.
181      * This constructor writes the serialization stream header to the
182      * underlying stream; callers may wish to flush the stream immediately to
183      * ensure that constructors for receiving ObjectInputStreams will not block
184      * when reading the header.
185      *
186      * <p>If a security manager is installed, this constructor will check for
187      * the "enableSubclassImplementation" SerializablePermission when invoked
188      * directly or indirectly by the constructor of a subclass which overrides
189      * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
190      * methods.
191      *
192      * @param out output stream to write to
193      * @throws IOException if an I/O error occurs while writing stream header
194      * @throws SecurityException if untrusted subclass illegally overrides
195      * security-sensitive methods
196      * @throws NullPointerException if <code>out</code> is <code>null</code>
197      * @see ObjectOutputStream#ObjectOutputStream()
198      * @see ObjectOutputStream#putFields()
199      * @see ObjectInputStream#ObjectInputStream(InputStream)
200      */

201     public ObjectOutputStream(OutputStream JavaDoc out) throws IOException JavaDoc {
202     verifySubclass();
203     bout = new BlockDataOutputStream(out);
204     handles = new HandleTable(10, (float) 3.00);
205     subs = new ReplaceTable(10, (float) 3.00);
206     enableOverride = false;
207     writeStreamHeader();
208     bout.setBlockDataMode(true);
209     }
210
211     /**
212      * Provide a way for subclasses that are completely reimplementing
213      * ObjectOutputStream to not have to allocate private data just used by
214      * this implementation of ObjectOutputStream.
215      *
216      * <p>If there is a security manager installed, this method first calls the
217      * security manager's <code>checkPermission</code> method with a
218      * <code>SerializablePermission("enableSubclassImplementation")</code>
219      * permission to ensure it's ok to enable subclassing.
220      *
221      * @throws SecurityException if a security manager exists and its
222      * <code>checkPermission</code> method denies enabling
223      * subclassing.
224      * @see SecurityManager#checkPermission
225      * @see java.io.SerializablePermission
226      */

227     protected ObjectOutputStream() throws IOException JavaDoc, SecurityException JavaDoc {
228     SecurityManager JavaDoc sm = System.getSecurityManager();
229     if (sm != null) {
230         sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
231     }
232     bout = null;
233     handles = null;
234     subs = null;
235     enableOverride = true;
236     }
237
238     /**
239      * Specify stream protocol version to use when writing the stream.
240      *
241      * <p>This routine provides a hook to enable the current version of
242      * Serialization to write in a format that is backwards compatible to a
243      * previous version of the stream format.
244      *
245      * <p>Every effort will be made to avoid introducing additional
246      * backwards incompatibilities; however, sometimes there is no
247      * other alternative.
248      *
249      * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
250      * @throws IllegalStateException if called after any objects
251      * have been serialized.
252      * @throws IllegalArgumentException if invalid version is passed in.
253      * @throws IOException if I/O errors occur
254      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
255      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
256      * @since 1.2
257      */

258     public void useProtocolVersion(int version) throws IOException JavaDoc {
259     if (handles.size() != 0) {
260         // REMIND: implement better check for pristine stream?
261
throw new IllegalStateException JavaDoc("stream non-empty");
262     }
263     switch (version) {
264         case PROTOCOL_VERSION_1:
265         case PROTOCOL_VERSION_2:
266         protocol = version;
267         break;
268         
269         default:
270         throw new IllegalArgumentException JavaDoc(
271             "unknown version: " + version);
272     }
273     }
274
275     /**
276      * Write the specified object to the ObjectOutputStream. The class of the
277      * object, the signature of the class, and the values of the non-transient
278      * and non-static fields of the class and all of its supertypes are
279      * written. Default serialization for a class can be overridden using the
280      * writeObject and the readObject methods. Objects referenced by this
281      * object are written transitively so that a complete equivalent graph of
282      * objects can be reconstructed by an ObjectInputStream.
283      *
284      * <p>Exceptions are thrown for problems with the OutputStream and for
285      * classes that should not be serialized. All exceptions are fatal to the
286      * OutputStream, which is left in an indeterminate state, and it is up to
287      * the caller to ignore or recover the stream state.
288      *
289      * @throws InvalidClassException Something is wrong with a class used by
290      * serialization.
291      * @throws NotSerializableException Some object to be serialized does not
292      * implement the java.io.Serializable interface.
293      * @throws IOException Any exception thrown by the underlying
294      * OutputStream.
295      */

296     public final void writeObject(Object JavaDoc obj) throws IOException JavaDoc {
297     if (enableOverride) {
298         writeObjectOverride(obj);
299         return;
300     }
301     try {
302         writeObject0(obj, false);
303     } catch (IOException JavaDoc ex) {
304         if (depth == 0) {
305         writeFatalException(ex);
306         }
307         throw ex;
308     }
309     }
310
311     /**
312      * Method used by subclasses to override the default writeObject method.
313      * This method is called by trusted subclasses of ObjectInputStream that
314      * constructed ObjectInputStream using the protected no-arg constructor.
315      * The subclass is expected to provide an override method with the modifier
316      * "final".
317      *
318      * @param obj object to be written to the underlying stream
319      * @throws IOException if there are I/O errors while writing to the
320      * underlying stream
321      * @see #ObjectOutputStream()
322      * @see #writeObject(Object)
323      * @since 1.2
324      */

325     protected void writeObjectOverride(Object JavaDoc obj) throws IOException JavaDoc {
326     }
327
328     /**
329      * Writes an "unshared" object to the ObjectOutputStream. This method is
330      * identical to writeObject, except that it always writes the given object
331      * as a new, unique object in the stream (as opposed to a back-reference
332      * pointing to a previously serialized instance). Specifically:
333      * <ul>
334      * <li>An object written via writeUnshared is always serialized in the
335      * same manner as a newly appearing object (an object that has not
336      * been written to the stream yet), regardless of whether or not the
337      * object has been written previously.
338      *
339      * <li>If writeObject is used to write an object that has been previously
340      * written with writeUnshared, the previous writeUnshared operation
341      * is treated as if it were a write of a separate object. In other
342      * words, ObjectOutputStream will never generate back-references to
343      * object data written by calls to writeUnshared.
344      * </ul>
345      * While writing an object via writeUnshared does not in itself guarantee a
346      * unique reference to the object when it is deserialized, it allows a
347      * single object to be defined multiple times in a stream, so that multiple
348      * calls to readUnshared by the receiver will not conflict. Note that the
349      * rules described above only apply to the base-level object written with
350      * writeUnshared, and not to any transitively referenced sub-objects in the
351      * object graph to be serialized.
352      *
353      * <p>ObjectOutputStream subclasses which override this method can only be
354      * constructed in security contexts possessing the
355      * "enableSubclassImplementation" SerializablePermission; any attempt to
356      * instantiate such a subclass without this permission will cause a
357      * SecurityException to be thrown.
358      *
359      * @param obj object to write to stream
360      * @throws NotSerializableException if an object in the graph to be
361      * serialized does not implement the Serializable interface
362      * @throws InvalidClassException if a problem exists with the class of an
363      * object to be serialized
364      * @throws IOException if an I/O error occurs during serialization
365      */

366     public void writeUnshared(Object JavaDoc obj) throws IOException JavaDoc {
367     try {
368         writeObject0(obj, true);
369     } catch (IOException JavaDoc ex) {
370         if (depth == 0) {
371         writeFatalException(ex);
372         }
373         throw ex;
374     }
375     }
376
377     /**
378      * Write the non-static and non-transient fields of the current class to
379      * this stream. This may only be called from the writeObject method of the
380      * class being serialized. It will throw the NotActiveException if it is
381      * called otherwise.
382      *
383      * @throws IOException if I/O errors occur while writing to the underlying
384      * <code>OutputStream</code>
385      */

386     public void defaultWriteObject() throws IOException JavaDoc {
387     if (curObj == null || curDesc == null) {
388         throw new NotActiveException JavaDoc("not in call to writeObject");
389     }
390     bout.setBlockDataMode(false);
391     defaultWriteFields(curObj, curDesc);
392     bout.setBlockDataMode(true);
393     }
394     
395     /**
396      * Retrieve the object used to buffer persistent fields to be written to
397      * the stream. The fields will be written to the stream when writeFields
398      * method is called.
399      *
400      * @return an instance of the class Putfield that holds the serializable
401      * fields
402      * @throws IOException if I/O errors occur
403      * @since 1.2
404      */

405     public ObjectOutputStream.PutField JavaDoc putFields() throws IOException JavaDoc {
406     if (curPut == null) {
407         if (curObj == null || curDesc == null) {
408         throw new NotActiveException JavaDoc("not in call to writeObject");
409         }
410         curPut = new PutFieldImpl(curDesc);
411     }
412     return curPut;
413     }
414
415     /**
416      * Write the buffered fields to the stream.
417      *
418      * @throws IOException if I/O errors occur while writing to the underlying
419      * stream
420      * @throws NotActiveException Called when a classes writeObject method was
421      * not called to write the state of the object.
422      * @since 1.2
423      */

424     public void writeFields() throws IOException JavaDoc {
425     if (curPut == null) {
426         throw new NotActiveException JavaDoc("no current PutField object");
427     }
428     bout.setBlockDataMode(false);
429     curPut.writeFields();
430     bout.setBlockDataMode(true);
431     }
432
433     /**
434      * Reset will disregard the state of any objects already written to the
435      * stream. The state is reset to be the same as a new ObjectOutputStream.
436      * The current point in the stream is marked as reset so the corresponding
437      * ObjectInputStream will be reset at the same point. Objects previously
438      * written to the stream will not be refered to as already being in the
439      * stream. They will be written to the stream again.
440      *
441      * @throws IOException if reset() is invoked while serializing an object.
442      */

443     public void reset() throws IOException JavaDoc {
444     if (depth != 0) {
445         throw new IOException JavaDoc("stream active");
446     }
447     bout.setBlockDataMode(false);
448     bout.writeByte(TC_RESET);
449     clear();
450     bout.setBlockDataMode(true);
451     }
452
453     /**
454      * Subclasses may implement this method to allow class data to be stored in
455      * the stream. By default this method does nothing. The corresponding
456      * method in ObjectInputStream is resolveClass. This method is called
457      * exactly once for each unique class in the stream. The class name and
458      * signature will have already been written to the stream. This method may
459      * make free use of the ObjectOutputStream to save any representation of
460      * the class it deems suitable (for example, the bytes of the class file).
461      * The resolveClass method in the corresponding subclass of
462      * ObjectInputStream must read and use any data or objects written by
463      * annotateClass.
464      *
465      * @param cl the class to annotate custom data for
466      * @throws IOException Any exception thrown by the underlying
467      * OutputStream.
468      */

469     protected void annotateClass(Class JavaDoc<?> cl) throws IOException JavaDoc {
470     }
471
472     /**
473      * Subclasses may implement this method to store custom data in the stream
474      * along with descriptors for dynamic proxy classes.
475      *
476      * <p>This method is called exactly once for each unique proxy class
477      * descriptor in the stream. The default implementation of this method in
478      * <code>ObjectOutputStream</code> does nothing.
479      *
480      * <p>The corresponding method in <code>ObjectInputStream</code> is
481      * <code>resolveProxyClass</code>. For a given subclass of
482      * <code>ObjectOutputStream</code> that overrides this method, the
483      * <code>resolveProxyClass</code> method in the corresponding subclass of
484      * <code>ObjectInputStream</code> must read any data or objects written by
485      * <code>annotateProxyClass</code>.
486      *
487      * @param cl the proxy class to annotate custom data for
488      * @throws IOException any exception thrown by the underlying
489      * <code>OutputStream</code>
490      * @see ObjectInputStream#resolveProxyClass(String[])
491      * @since 1.3
492      */

493     protected void annotateProxyClass(Class JavaDoc<?> cl) throws IOException JavaDoc {
494     }
495
496     /**
497      * This method will allow trusted subclasses of ObjectOutputStream to
498      * substitute one object for another during serialization. Replacing
499      * objects is disabled until enableReplaceObject is called. The
500      * enableReplaceObject method checks that the stream requesting to do
501      * replacement can be trusted. The first occurrence of each object written
502      * into the serialization stream is passed to replaceObject. Subsequent
503      * references to the object are replaced by the object returned by the
504      * original call to replaceObject. To ensure that the private state of
505      * objects is not unintentionally exposed, only trusted streams may use
506      * replaceObject.
507      *
508      * <p>The ObjectOutputStream.writeObject method takes a parameter of type
509      * Object (as opposed to type Serializable) to allow for cases where
510      * non-serializable objects are replaced by serializable ones.
511      *
512      * <p>When a subclass is replacing objects it must insure that either a
513      * complementary substitution must be made during deserialization or that
514      * the substituted object is compatible with every field where the
515      * reference will be stored. Objects whose type is not a subclass of the
516      * type of the field or array element abort the serialization by raising an
517      * exception and the object is not be stored.
518      *
519      * <p>This method is called only once when each object is first
520      * encountered. All subsequent references to the object will be redirected
521      * to the new object. This method should return the object to be
522      * substituted or the original object.
523      *
524      * <p>Null can be returned as the object to be substituted, but may cause
525      * NullReferenceException in classes that contain references to the
526      * original object since they may be expecting an object instead of
527      * null.
528      *
529      * @param obj the object to be replaced
530      * @return the alternate object that replaced the specified one
531      * @throws IOException Any exception thrown by the underlying
532      * OutputStream.
533      */

534     protected Object JavaDoc replaceObject(Object JavaDoc obj) throws IOException JavaDoc {
535     return obj;
536     }
537
538     /**
539      * Enable the stream to do replacement of objects in the stream. When
540      * enabled, the replaceObject method is called for every object being
541      * serialized.
542      *
543      * <p>If <code>enable</code> is true, and there is a security manager
544      * installed, this method first calls the security manager's
545      * <code>checkPermission</code> method with a
546      * <code>SerializablePermission("enableSubstitution")</code> permission to
547      * ensure it's ok to enable the stream to do replacement of objects in the
548      * stream.
549      *
550      * @param enable boolean parameter to enable replacement of objects
551      * @return the previous setting before this method was invoked
552      * @throws SecurityException if a security manager exists and its
553      * <code>checkPermission</code> method denies enabling the stream
554      * to do replacement of objects in the stream.
555      * @see SecurityManager#checkPermission
556      * @see java.io.SerializablePermission
557      */

558     protected boolean enableReplaceObject(boolean enable)
559     throws SecurityException JavaDoc
560     {
561     if (enable == enableReplace) {
562         return enable;
563     }
564     if (enable) {
565         SecurityManager JavaDoc sm = System.getSecurityManager();
566         if (sm != null) {
567         sm.checkPermission(SUBSTITUTION_PERMISSION);
568         }
569     }
570     enableReplace = enable;
571     return !enableReplace;
572     }
573
574     /**
575      * The writeStreamHeader method is provided so subclasses can append or
576      * prepend their own header to the stream. It writes the magic number and
577      * version to the stream.
578      *
579      * @throws IOException if I/O errors occur while writing to the underlying
580      * stream
581      */

582     protected void writeStreamHeader() throws IOException JavaDoc {
583     bout.writeShort(STREAM_MAGIC);
584     bout.writeShort(STREAM_VERSION);
585     }
586
587     /**
588      * Write the specified class descriptor to the ObjectOutputStream. Class
589      * descriptors are used to identify the classes of objects written to the
590      * stream. Subclasses of ObjectOutputStream may override this method to
591      * customize the way in which class descriptors are written to the
592      * serialization stream. The corresponding method in ObjectInputStream,
593      * <code>readClassDescriptor</code>, should then be overridden to
594      * reconstitute the class descriptor from its custom stream representation.
595      * By default, this method writes class descriptors according to the format
596      * defined in the Object Serialization specification.
597      *
598      * <p>Note that this method will only be called if the ObjectOutputStream
599      * is not using the old serialization stream format (set by calling
600      * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
601      * serialization stream is using the old format
602      * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
603      * internally in a manner that cannot be overridden or customized.
604      *
605      * @param desc class descriptor to write to the stream
606      * @throws IOException If an I/O error has occurred.
607      * @see java.io.ObjectInputStream#readClassDescriptor()
608      * @see #useProtocolVersion(int)
609      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
610      * @since 1.3
611      */

612     protected void writeClassDescriptor(ObjectStreamClass JavaDoc desc)
613     throws IOException JavaDoc
614     {
615     desc.writeNonProxy(this);
616     }
617
618     /**
619      * Writes a byte. This method will block until the byte is actually
620      * written.
621      *
622      * @param val the byte to be written to the stream
623      * @throws IOException If an I/O error has occurred.
624      */

625     public void write(int val) throws IOException JavaDoc {
626     bout.write(val);
627     }
628
629     /**
630      * Writes an array of bytes. This method will block until the bytes are
631      * actually written.
632      *
633      * @param buf the data to be written
634      * @throws IOException If an I/O error has occurred.
635      */

636     public void write(byte[] buf) throws IOException JavaDoc {
637     bout.write(buf, 0, buf.length, false);
638     }
639
640     /**
641      * Writes a sub array of bytes.
642      *
643      * @param buf the data to be written
644      * @param off the start offset in the data
645      * @param len the number of bytes that are written
646      * @throws IOException If an I/O error has occurred.
647      */

648     public void write(byte[] buf, int off, int len) throws IOException JavaDoc {
649     if (buf == null) {
650         throw new NullPointerException JavaDoc();
651     }
652     int endoff = off + len;
653     if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
654         throw new IndexOutOfBoundsException JavaDoc();
655     }
656     bout.write(buf, off, len, false);
657     }
658
659     /**
660      * Flushes the stream. This will write any buffered output bytes and flush
661      * through to the underlying stream.
662      *
663      * @throws IOException If an I/O error has occurred.
664      */

665     public void flush() throws IOException JavaDoc {
666     bout.flush();
667     }
668
669     /**
670      * Drain any buffered data in ObjectOutputStream. Similar to flush but
671      * does not propagate the flush to the underlying stream.
672      *
673      * @throws IOException if I/O errors occur while writing to the underlying
674      * stream
675      */

676     protected void drain() throws IOException JavaDoc {
677     bout.drain();
678     }
679
680     /**
681      * Closes the stream. This method must be called to release any resources
682      * associated with the stream.
683      *
684      * @throws IOException If an I/O error has occurred.
685      */

686     public void close() throws IOException JavaDoc {
687     flush();
688     clear();
689     bout.close();
690     }
691
692     /**
693      * Writes a boolean.
694      *
695      * @param val the boolean to be written
696      * @throws IOException if I/O errors occur while writing to the underlying
697      * stream
698      */

699     public void writeBoolean(boolean val) throws IOException JavaDoc {
700     bout.writeBoolean(val);
701     }
702
703     /**
704      * Writes an 8 bit byte.
705      *
706      * @param val the byte value to be written
707      * @throws IOException if I/O errors occur while writing to the underlying
708      * stream
709      */

710     public void writeByte(int val) throws IOException JavaDoc {
711     bout.writeByte(val);
712     }
713
714     /**
715      * Writes a 16 bit short.
716      *
717      * @param val the short value to be written
718      * @throws IOException if I/O errors occur while writing to the underlying
719      * stream
720      */

721     public void writeShort(int val) throws IOException JavaDoc {
722     bout.writeShort(val);
723     }
724
725     /**
726      * Writes a 16 bit char.
727      *
728      * @param val the char value to be written
729      * @throws IOException if I/O errors occur while writing to the underlying
730      * stream
731      */

732     public void writeChar(int val) throws IOException JavaDoc {
733     bout.writeChar(val);
734     }
735
736     /**
737      * Writes a 32 bit int.
738      *
739      * @param val the integer value to be written
740      * @throws IOException if I/O errors occur while writing to the underlying
741      * stream
742      */

743     public void writeInt(int val) throws IOException JavaDoc {
744     bout.writeInt(val);
745     }
746
747     /**
748      * Writes a 64 bit long.
749      *
750      * @param val the long value to be written
751      * @throws IOException if I/O errors occur while writing to the underlying
752      * stream
753      */

754     public void writeLong(long val) throws IOException JavaDoc {
755     bout.writeLong(val);
756     }
757
758     /**
759      * Writes a 32 bit float.
760      *
761      * @param val the float value to be written
762      * @throws IOException if I/O errors occur while writing to the underlying
763      * stream
764      */

765     public void writeFloat(float val) throws IOException JavaDoc {
766     bout.writeFloat(val);
767     }
768
769     /**
770      * Writes a 64 bit double.
771      *
772      * @param val the double value to be written
773      * @throws IOException if I/O errors occur while writing to the underlying
774      * stream
775      */

776     public void writeDouble(double val) throws IOException JavaDoc {
777     bout.writeDouble(val);
778     }
779
780     /**
781      * Writes a String as a sequence of bytes.
782      *
783      * @param str the String of bytes to be written
784      * @throws IOException if I/O errors occur while writing to the underlying
785      * stream
786      */

787     public void writeBytes(String JavaDoc str) throws IOException JavaDoc {
788     bout.writeBytes(str);
789     }
790
791     /**
792      * Writes a String as a sequence of chars.
793      *
794      * @param str the String of chars to be written
795      * @throws IOException if I/O errors occur while writing to the underlying
796      * stream
797      */

798     public void writeChars(String JavaDoc str) throws IOException JavaDoc {
799     bout.writeChars(str);
800     }
801
802     /**
803      * Primitive data write of this String in
804      * <a HREF="DataInput.html#modified-utf-8">modified UTF-8</a>
805      * format. Note that there is a
806      * significant difference between writing a String into the stream as
807      * primitive data or as an Object. A String instance written by writeObject
808      * is written into the stream as a String initially. Future writeObject()
809      * calls write references to the string into the stream.
810      *
811      * @param str the String to be written
812      * @throws IOException if I/O errors occur while writing to the underlying
813      * stream
814      */

815     public void writeUTF(String JavaDoc str) throws IOException JavaDoc {
816     bout.writeUTF(str);
817     }
818     
819     /**
820      * Provide programmatic access to the persistent fields to be written
821      * to ObjectOutput.
822      *
823      * @since 1.2
824      */

825     public static abstract class PutField {
826
827     /**
828      * Put the value of the named boolean field into the persistent field.
829      *
830      * @param name the name of the serializable field
831      * @param val the value to assign to the field
832      */

833     public abstract void put(String JavaDoc name, boolean val);
834
835     /**
836      * Put the value of the named byte field into the persistent field.
837      *
838      * @param name the name of the serializable field
839      * @param val the value to assign to the field
840      */

841     public abstract void put(String JavaDoc name, byte val);
842
843     /**
844      * Put the value of the named char field into the persistent field.
845      *
846      * @param name the name of the serializable field
847      * @param val the value to assign to the field
848      */

849     public abstract void put(String JavaDoc name, char val);
850
851     /**
852      * Put the value of the named short field into the persistent field.
853      *
854      * @param name the name of the serializable field
855      * @param val the value to assign to the field
856      */

857     public abstract void put(String JavaDoc name, short val);
858
859     /**
860      * Put the value of the named int field into the persistent field.
861      *
862      * @param name the name of the serializable field
863      * @param val the value to assign to the field
864      */

865     public abstract void put(String JavaDoc name, int val);
866
867     /**
868      * Put the value of the named long field into the persistent field.
869      *
870      * @param name the name of the serializable field
871      * @param val the value to assign to the field
872      */

873     public abstract void put(String JavaDoc name, long val);
874
875     /**
876      * Put the value of the named float field into the persistent field.
877      *
878      * @param name the name of the serializable field
879      * @param val the value to assign to the field
880      */

881     public abstract void put(String JavaDoc name, float val);
882
883     /**
884      * Put the value of the named double field into the persistent field.
885      *
886      * @param name the name of the serializable field
887      * @param val the value to assign to the field
888      */

889     public abstract void put(String JavaDoc name, double val);
890
891     /**
892      * Put the value of the named Object field into the persistent field.
893      *
894      * @param name the name of the serializable field
895      * @param val the value to assign to the field
896      */

897     public abstract void put(String JavaDoc name, Object JavaDoc val);
898
899     /**
900      * Write the data and fields to the specified ObjectOutput stream.
901      *
902      * @param out the stream to write the data and fields to
903      * @throws IOException if I/O errors occur while writing to the
904      * underlying stream
905      * @deprecated This method does not write the values contained by this
906      * <code>PutField</code> object in a proper format, and may
907      * result in corruption of the serialization stream. The
908      * correct way to write <code>PutField</code> data is by
909      * calling the {@link java.io.ObjectOutputStream#writeFields()}
910      * method.
911      */

912         @Deprecated JavaDoc
913     public abstract void write(ObjectOutput JavaDoc out) throws IOException JavaDoc;
914     }
915
916
917     /**
918      * Returns protocol version in use.
919      */

920     int getProtocolVersion() {
921     return protocol;
922     }
923
924     /**
925      * Writes string without allowing it to be replaced in stream. Used by
926      * ObjectStreamClass to write class descriptor type strings.
927      */

928     void writeTypeString(String JavaDoc str) throws IOException JavaDoc {
929     int handle;
930     if (str == null) {
931         writeNull();
932     } else if ((handle = handles.lookup(str)) != -1) {
933         writeHandle(handle);
934     } else {
935         writeString(str, false);
936     }
937     }
938     
939     /**
940      * Verifies that this (possibly subclass) instance can be constructed
941      * without violating security constraints: the subclass must not override
942      * security-sensitive non-final methods, or else the
943      * "enableSubclassImplementation" SerializablePermission is checked.
944      */

945     private void verifySubclass() {
946     Class JavaDoc cl = getClass();
947     processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
948     WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
949     Boolean JavaDoc result = Caches.subclassAudits.get(key);
950     if (result == null) {
951         result = Boolean.valueOf(auditSubclass(cl));
952         Caches.subclassAudits.putIfAbsent(key, result);
953     }
954     if (result.booleanValue()) {
955         return;
956     }
957     SecurityManager JavaDoc sm = System.getSecurityManager();
958     if (sm != null) {
959         sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
960     }
961     }
962     
963     /**
964      * Performs reflective checks on given subclass to verify that it doesn't
965      * override security-sensitive non-final methods. Returns true if subclass
966      * is "safe", false otherwise.
967      */

968     private static boolean auditSubclass(final Class JavaDoc subcl) {
969     Boolean JavaDoc result = (Boolean JavaDoc) AccessController.doPrivileged(
970         new PrivilegedAction JavaDoc() {
971         public Object JavaDoc run() {
972             for (Class JavaDoc cl = subcl;
973              cl != ObjectOutputStream JavaDoc.class;
974              cl = cl.getSuperclass())
975             {
976             try {
977                 cl.getDeclaredMethod(
978                 "writeUnshared", new Class JavaDoc[] { Object JavaDoc.class });
979                 return Boolean.FALSE;
980             } catch (NoSuchMethodException JavaDoc ex) {
981             }
982             try {
983                 cl.getDeclaredMethod("putFields", new Class JavaDoc[0]);
984                 return Boolean.FALSE;
985             } catch (NoSuchMethodException JavaDoc ex) {
986             }
987             }
988             return Boolean.TRUE;
989         }
990         }
991     );
992     return result.booleanValue();
993     }
994
995     /**
996      * Clears internal data structures.
997      */

998     private void clear() {
999     subs.clear();
1000    handles.clear();
1001    }
1002
1003    /**
1004     * Underlying writeObject/writeUnshared implementation.
1005     */

1006    private void writeObject0(Object JavaDoc obj, boolean unshared)
1007    throws IOException JavaDoc
1008    {
1009    boolean oldMode = bout.setBlockDataMode(false);
1010    depth++;
1011    try {
1012        // handle previously written and non-replaceable objects
1013
int h;
1014        if ((obj = subs.lookup(obj)) == null) {
1015        writeNull();
1016        return;
1017        } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1018        writeHandle(h);
1019        return;
1020        } else if (obj instanceof Class JavaDoc) {
1021        writeClass((Class JavaDoc) obj, unshared);
1022        return;
1023        } else if (obj instanceof ObjectStreamClass JavaDoc) {
1024        writeClassDesc((ObjectStreamClass JavaDoc) obj, unshared);
1025        return;
1026        }
1027        
1028        // check for replacement object
1029
Object JavaDoc orig = obj;
1030        Class JavaDoc cl = obj.getClass();
1031        ObjectStreamClass JavaDoc desc;
1032        for (;;) {
1033        // REMIND: skip this check for strings/arrays?
1034
Class JavaDoc repCl;
1035        desc = ObjectStreamClass.lookup(cl, true);
1036        if (!desc.hasWriteReplaceMethod() ||
1037            (obj = desc.invokeWriteReplace(obj)) == null ||
1038            (repCl = obj.getClass()) == cl)
1039        {
1040            break;
1041        }
1042        cl = repCl;
1043        }
1044        if (enableReplace) {
1045        Object JavaDoc rep = replaceObject(obj);
1046        if (rep != obj && rep != null) {
1047            cl = rep.getClass();
1048            desc = ObjectStreamClass.lookup(cl, true);
1049        }
1050        obj = rep;
1051        }
1052
1053        // if object replaced, run through original checks a second time
1054
if (obj != orig) {
1055        subs.assign(orig, obj);
1056        if (obj == null) {
1057            writeNull();
1058            return;
1059        } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1060            writeHandle(h);
1061            return;
1062        } else if (obj instanceof Class JavaDoc) {
1063            writeClass((Class JavaDoc) obj, unshared);
1064            return;
1065        } else if (obj instanceof ObjectStreamClass JavaDoc) {
1066            writeClassDesc((ObjectStreamClass JavaDoc) obj, unshared);
1067            return;
1068        }
1069        }
1070        
1071        // remaining cases
1072
if (obj instanceof String JavaDoc) {
1073        writeString((String JavaDoc) obj, unshared);
1074        } else if (cl.isArray()) {
1075        writeArray(obj, desc, unshared);
1076        } else if (obj instanceof Enum JavaDoc) {
1077        writeEnum((Enum JavaDoc) obj, desc, unshared);
1078        } else if (obj instanceof Serializable JavaDoc) {
1079        writeOrdinaryObject(obj, desc, unshared);
1080        } else {
1081        throw new NotSerializableException JavaDoc(cl.getName());
1082        }
1083    } finally {
1084        depth--;
1085        bout.setBlockDataMode(oldMode);
1086    }
1087    }
1088    
1089    /**
1090     * Writes null code to stream.
1091     */

1092    private void writeNull() throws IOException JavaDoc {
1093    bout.writeByte(TC_NULL);
1094    }
1095    
1096    /**
1097     * Writes given object handle to stream.
1098     */

1099    private void writeHandle(int handle) throws IOException JavaDoc {
1100    bout.writeByte(TC_REFERENCE);
1101    bout.writeInt(baseWireHandle + handle);
1102    }
1103    
1104    /**
1105     * Writes representation of given class to stream.
1106     */

1107    private void writeClass(Class JavaDoc cl, boolean unshared) throws IOException JavaDoc {
1108    bout.writeByte(TC_CLASS);
1109    writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1110    handles.assign(unshared ? null : cl);
1111    }
1112    
1113    /**
1114     * Writes representation of given class descriptor to stream.
1115     */

1116    private void writeClassDesc(ObjectStreamClass JavaDoc desc, boolean unshared)
1117    throws IOException JavaDoc
1118    {
1119    int handle;
1120    if (desc == null) {
1121        writeNull();
1122    } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1123        writeHandle(handle);
1124    } else if (desc.isProxy()) {
1125        writeProxyDesc(desc, unshared);
1126    } else {
1127        writeNonProxyDesc(desc, unshared);
1128    }
1129    }
1130    
1131    /**
1132     * Writes class descriptor representing a dynamic proxy class to stream.
1133     */

1134    private void writeProxyDesc(ObjectStreamClass JavaDoc desc, boolean unshared)
1135    throws IOException JavaDoc
1136    {
1137    bout.writeByte(TC_PROXYCLASSDESC);
1138    handles.assign(unshared ? null : desc);
1139
1140    Class JavaDoc cl = desc.forClass();
1141    Class JavaDoc[] ifaces = cl.getInterfaces();
1142    bout.writeInt(ifaces.length);
1143    for (int i = 0; i < ifaces.length; i++) {
1144        bout.writeUTF(ifaces[i].getName());
1145    }
1146    
1147    bout.setBlockDataMode(true);
1148    annotateProxyClass(cl);
1149    bout.setBlockDataMode(false);
1150    bout.writeByte(TC_ENDBLOCKDATA);
1151    
1152    writeClassDesc(desc.getSuperDesc(), false);
1153    }
1154    
1155    /**
1156     * Writes class descriptor representing a standard (i.e., not a dynamic
1157     * proxy) class to stream.
1158     */

1159    private void writeNonProxyDesc(ObjectStreamClass JavaDoc desc, boolean unshared)
1160    throws IOException JavaDoc
1161    {
1162    bout.writeByte(TC_CLASSDESC);
1163    handles.assign(unshared ? null : desc);
1164    
1165    if (protocol == PROTOCOL_VERSION_1) {
1166        // do not invoke class descriptor write hook with old protocol
1167
desc.writeNonProxy(this);
1168    } else {
1169        writeClassDescriptor(desc);
1170    }
1171    
1172    Class JavaDoc cl = desc.forClass();
1173    bout.setBlockDataMode(true);
1174    annotateClass(cl);
1175    bout.setBlockDataMode(false);
1176    bout.writeByte(TC_ENDBLOCKDATA);
1177    
1178    writeClassDesc(desc.getSuperDesc(), false);
1179    }
1180    
1181    /**
1182     * Writes given string to stream, using standard or long UTF format
1183     * depending on string length.
1184     */

1185    private void writeString(String JavaDoc str, boolean unshared) throws IOException JavaDoc {
1186    handles.assign(unshared ? null : str);
1187    long utflen = bout.getUTFLength(str);
1188    if (utflen <= 0xFFFF) {
1189        bout.writeByte(TC_STRING);
1190        bout.writeUTF(str, utflen);
1191    } else {
1192        bout.writeByte(TC_LONGSTRING);
1193        bout.writeLongUTF(str, utflen);
1194    }
1195    }
1196    
1197    /**
1198     * Writes given array object to stream.
1199     */

1200    private void writeArray(Object JavaDoc array,
1201                ObjectStreamClass JavaDoc desc,
1202                boolean unshared)
1203    throws IOException JavaDoc
1204    {
1205    bout.writeByte(TC_ARRAY);
1206    writeClassDesc(desc, false);
1207    handles.assign(unshared ? null : array);
1208    
1209    Class JavaDoc ccl = desc.forClass().getComponentType();
1210    if (ccl.isPrimitive()) {
1211        if (ccl == Integer.TYPE) {
1212        int[] ia = (int[]) array;
1213        bout.writeInt(ia.length);
1214        bout.writeInts(ia, 0, ia.length);
1215        } else if (ccl == Byte.TYPE) {
1216        byte[] ba = (byte[]) array;
1217        bout.writeInt(ba.length);
1218        bout.write(ba, 0, ba.length, true);
1219        } else if (ccl == Long.TYPE) {
1220        long[] ja = (long[]) array;
1221        bout.writeInt(ja.length);
1222        bout.writeLongs(ja, 0, ja.length);
1223        } else if (ccl == Float.TYPE) {
1224        float[] fa = (float[]) array;
1225        bout.writeInt(fa.length);
1226        bout.writeFloats(fa, 0, fa.length);
1227        } else if (ccl == Double.TYPE) {
1228        double[] da = (double[]) array;
1229        bout.writeInt(da.length);
1230        bout.writeDoubles(da, 0, da.length);
1231        } else if (ccl == Short.TYPE) {
1232        short[] sa = (short[]) array;
1233        bout.writeInt(sa.length);
1234        bout.writeShorts(sa, 0, sa.length);
1235        } else if (ccl == Character.TYPE) {
1236        char[] ca = (char[]) array;
1237        bout.writeInt(ca.length);
1238        bout.writeChars(ca, 0, ca.length);
1239        } else if (ccl == Boolean.TYPE) {
1240        boolean[] za = (boolean[]) array;
1241        bout.writeInt(za.length);
1242        bout.writeBooleans(za, 0, za.length);
1243        } else {
1244        throw new InternalError JavaDoc();
1245        }
1246    } else {
1247        Object JavaDoc[] objs = (Object JavaDoc[]) array;
1248        int len = objs.length;
1249        bout.writeInt(len);
1250        for (int i = 0; i < len; i++) {
1251        writeObject0(objs[i], false);
1252        }
1253    }
1254    }
1255    
1256    /**
1257     * Writes given enum constant to stream.
1258     */

1259    private void writeEnum(Enum JavaDoc en,
1260               ObjectStreamClass JavaDoc desc,
1261               boolean unshared)
1262    throws IOException JavaDoc
1263    {
1264    bout.writeByte(TC_ENUM);
1265    ObjectStreamClass JavaDoc sdesc = desc.getSuperDesc();
1266    writeClassDesc((sdesc.forClass() == Enum JavaDoc.class) ? desc : sdesc, false);
1267    handles.assign(unshared ? null : en);
1268    writeString(en.name(), false);
1269    }
1270
1271    /**
1272     * Writes representation of a "ordinary" (i.e., not a String, Class,
1273     * ObjectStreamClass, array, or enum constant) serializable object to the
1274     * stream.
1275     */

1276    private void writeOrdinaryObject(Object JavaDoc obj,
1277                     ObjectStreamClass JavaDoc desc,
1278                     boolean unshared)
1279    throws IOException JavaDoc
1280    {
1281    desc.checkSerialize();
1282
1283    bout.writeByte(TC_OBJECT);
1284    writeClassDesc(desc, false);
1285    handles.assign(unshared ? null : obj);
1286
1287    if (desc.isExternalizable() && !desc.isProxy()) {
1288        writeExternalData((Externalizable JavaDoc) obj);
1289    } else {
1290        writeSerialData(obj, desc);
1291    }
1292    }
1293    
1294    /**
1295     * Writes externalizable data of given object by invoking its
1296     * writeExternal() method.
1297     */

1298    private void writeExternalData(Externalizable JavaDoc obj) throws IOException JavaDoc {
1299    Object JavaDoc oldObj = curObj;
1300    ObjectStreamClass JavaDoc oldDesc = curDesc;
1301    PutFieldImpl oldPut = curPut;
1302    curObj = obj;
1303    curDesc = null;
1304    curPut = null;
1305    
1306    if (protocol == PROTOCOL_VERSION_1) {
1307        obj.writeExternal(this);
1308    } else {
1309        bout.setBlockDataMode(true);
1310        obj.writeExternal(this);
1311        bout.setBlockDataMode(false);
1312        bout.writeByte(TC_ENDBLOCKDATA);
1313    }
1314
1315    curObj = oldObj;
1316    curDesc = oldDesc;
1317    curPut = oldPut;
1318    }
1319    
1320    /**
1321     * Writes instance data for each serializable class of given object, from
1322     * superclass to subclass.
1323     */

1324    private void writeSerialData(Object JavaDoc obj, ObjectStreamClass JavaDoc desc)
1325    throws IOException JavaDoc
1326    {
1327    ObjectStreamClass.ClassDataSlot JavaDoc[] slots = desc.getClassDataLayout();
1328    for (int i = 0; i < slots.length; i++) {
1329        ObjectStreamClass JavaDoc slotDesc = slots[i].desc;
1330        if (slotDesc.hasWriteObjectMethod()) {
1331        Object JavaDoc oldObj = curObj;
1332        ObjectStreamClass JavaDoc oldDesc = curDesc;
1333        PutFieldImpl oldPut = curPut;
1334        curObj = obj;
1335        curDesc = slotDesc;
1336        curPut = null;
1337
1338        bout.setBlockDataMode(true);
1339        slotDesc.invokeWriteObject(obj, this);
1340        bout.setBlockDataMode(false);
1341        bout.writeByte(TC_ENDBLOCKDATA);
1342
1343        curObj = oldObj;
1344        curDesc = oldDesc;
1345        curPut = oldPut;
1346        } else {
1347        defaultWriteFields(obj, slotDesc);
1348        }
1349    }
1350    }
1351    
1352    /**
1353     * Fetches and writes values of serializable fields of given object to
1354     * stream. The given class descriptor specifies which field values to
1355     * write, and in which order they should be written.
1356     */

1357    private void defaultWriteFields(Object JavaDoc obj, ObjectStreamClass JavaDoc desc)
1358    throws IOException JavaDoc
1359    {
1360    // REMIND: perform conservative isInstance check here?
1361
desc.checkDefaultSerialize();
1362
1363    int primDataSize = desc.getPrimDataSize();
1364    if (primVals == null || primVals.length < primDataSize) {
1365        primVals = new byte[primDataSize];
1366    }
1367    desc.getPrimFieldValues(obj, primVals);
1368    bout.write(primVals, 0, primDataSize, false);
1369    
1370    ObjectStreamField JavaDoc[] fields = desc.getFields(false);
1371    Object JavaDoc[] objVals = new Object JavaDoc[desc.getNumObjFields()];
1372    int numPrimFields = fields.length - objVals.length;
1373    desc.getObjFieldValues(obj, objVals);
1374    for (int i = 0; i < objVals.length; i++) {
1375        writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());
1376    }
1377    }
1378
1379    /**
1380     * Attempts to write to stream fatal IOException that has caused
1381     * serialization to abort.
1382     */

1383    private void writeFatalException(IOException JavaDoc ex) throws IOException JavaDoc {
1384    /*
1385     * Note: the serialization specification states that if a second
1386     * IOException occurs while attempting to serialize the original fatal
1387     * exception to the stream, then a StreamCorruptedException should be
1388     * thrown (section 2.1). However, due to a bug in previous
1389     * implementations of serialization, StreamCorruptedExceptions were
1390     * rarely (if ever) actually thrown--the "root" exceptions from
1391     * underlying streams were thrown instead. This historical behavior is
1392     * followed here for consistency.
1393     */

1394    clear();
1395    boolean oldMode = bout.setBlockDataMode(false);
1396    try {
1397        bout.writeByte(TC_EXCEPTION);
1398        writeObject0(ex, false);
1399        clear();
1400    } finally {
1401        bout.setBlockDataMode(oldMode);
1402    }
1403    }
1404
1405    /**
1406     * Converts specified span of float values into byte values.
1407     */

1408    // REMIND: remove once hotspot inlines Float.floatToIntBits
1409
private static native void floatsToBytes(float[] src, int srcpos,
1410                         byte[] dst, int dstpos,
1411                         int nfloats);
1412
1413    /**
1414     * Converts specified span of double values into byte values.
1415     */

1416    // REMIND: remove once hotspot inlines Double.doubleToLongBits
1417
private static native void doublesToBytes(double[] src, int srcpos,
1418                          byte[] dst, int dstpos,
1419                          int ndoubles);
1420
1421    /**
1422     * Default PutField implementation.
1423     */

1424    private class PutFieldImpl extends PutField {
1425
1426    /** class descriptor describing serializable fields */
1427    private final ObjectStreamClass JavaDoc desc;
1428    /** primitive field values */
1429    private final byte[] primVals;
1430    /** object field values */
1431    private final Object JavaDoc[] objVals;
1432
1433    /**
1434     * Creates PutFieldImpl object for writing fields defined in given
1435     * class descriptor.
1436     */

1437    PutFieldImpl(ObjectStreamClass JavaDoc desc) {
1438        this.desc = desc;
1439        primVals = new byte[desc.getPrimDataSize()];
1440        objVals = new Object JavaDoc[desc.getNumObjFields()];
1441    }
1442
1443    public void put(String JavaDoc name, boolean val) {
1444        Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
1445    }
1446
1447    public void put(String JavaDoc name, byte val) {
1448        primVals[getFieldOffset(name, Byte.TYPE)] = val;
1449    }
1450
1451    public void put(String JavaDoc name, char val) {
1452        Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
1453    }
1454
1455    public void put(String JavaDoc name, short val) {
1456        Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
1457    }
1458
1459    public void put(String JavaDoc name, int val) {
1460        Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
1461    }
1462
1463    public void put(String JavaDoc name, float val) {
1464        Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
1465    }
1466
1467    public void put(String JavaDoc name, long val) {
1468        Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
1469    }
1470
1471    public void put(String JavaDoc name, double val) {
1472        Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
1473    }
1474
1475    public void put(String JavaDoc name, Object JavaDoc val) {
1476        objVals[getFieldOffset(name, Object JavaDoc.class)] = val;
1477    }
1478
1479    // deprecated in ObjectOutputStream.PutField
1480
public void write(ObjectOutput JavaDoc out) throws IOException JavaDoc {
1481        /*
1482         * Applications should *not* use this method to write PutField
1483         * data, as it will lead to stream corruption if the PutField
1484         * object writes any primitive data (since block data mode is not
1485         * unset/set properly, as is done in OOS.writeFields()). This
1486         * broken implementation is being retained solely for behavioral
1487         * compatibility, in order to support applications which use
1488         * OOS.PutField.write() for writing only non-primitive data.
1489         *
1490         * Serialization of unshared objects is not implemented here since
1491         * it is not necessary for backwards compatibility; also, unshared
1492         * semantics may not be supported by the given ObjectOutput
1493         * instance. Applications which write unshared objects using the
1494         * PutField API must use OOS.writeFields().
1495         */

1496        if (ObjectOutputStream.this != out) {
1497        throw new IllegalArgumentException JavaDoc("wrong stream");
1498        }
1499        out.write(primVals, 0, primVals.length);
1500        
1501        ObjectStreamField JavaDoc[] fields = desc.getFields(false);
1502        int numPrimFields = fields.length - objVals.length;
1503        // REMIND: warn if numPrimFields > 0?
1504
for (int i = 0; i < objVals.length; i++) {
1505        if (fields[numPrimFields + i].isUnshared()) {
1506            throw new IOException JavaDoc("cannot write unshared object");
1507        }
1508        out.writeObject(objVals[i]);
1509        }
1510    }
1511    
1512    /**
1513     * Writes buffered primitive data and object fields to stream.
1514     */

1515    void writeFields() throws IOException JavaDoc {
1516        bout.write(primVals, 0, primVals.length, false);
1517
1518        ObjectStreamField JavaDoc[] fields = desc.getFields(false);
1519        int numPrimFields = fields.length - objVals.length;
1520        for (int i = 0; i < objVals.length; i++) {
1521        writeObject0(
1522            objVals[i], fields[numPrimFields + i].isUnshared());
1523        }
1524    }
1525    
1526    /**
1527     * Returns offset of field with given name and type. A specified type
1528     * of null matches all types, Object.class matches all non-primitive
1529     * types, and any other non-null type matches assignable types only.
1530     * Throws IllegalArgumentException if no matching field found.
1531     */

1532    private int getFieldOffset(String JavaDoc name, Class JavaDoc type) {
1533        ObjectStreamField JavaDoc field = desc.getField(name, type);
1534        if (field == null) {
1535        throw new IllegalArgumentException JavaDoc("no such field");
1536        }
1537        return field.getOffset();
1538    }
1539    }
1540
1541    /**
1542     * Buffered output stream with two modes: in default mode, outputs data in
1543     * same format as DataOutputStream; in "block data" mode, outputs data
1544     * bracketed by block data markers (see object serialization specification
1545     * for details).
1546     */

1547    private static class BlockDataOutputStream
1548    extends OutputStream JavaDoc implements DataOutput JavaDoc
1549    {
1550    /** maximum data block length */
1551    private static final int MAX_BLOCK_SIZE = 1024;
1552    /** maximum data block header length */
1553    private static final int MAX_HEADER_SIZE = 5;
1554    /** (tunable) length of char buffer (for writing strings) */
1555    private static final int CHAR_BUF_SIZE = 256;
1556
1557    /** buffer for writing general/block data */
1558    private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1559    /** buffer for writing block data headers */
1560    private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1561    /** char buffer for fast string writes */
1562    private final char[] cbuf = new char[CHAR_BUF_SIZE];
1563    
1564    /** block data mode */
1565    private boolean blkmode = false;
1566    /** current offset into buf */
1567    private int pos = 0;
1568
1569    /** underlying output stream */
1570    private final OutputStream JavaDoc out;
1571    /** loopback stream (for data writes that span data blocks) */
1572    private final DataOutputStream JavaDoc dout;
1573
1574    /**
1575     * Creates new BlockDataOutputStream on top of given underlying stream.
1576     * Block data mode is turned off by default.
1577     */

1578    BlockDataOutputStream(OutputStream JavaDoc out) {
1579        this.out = out;
1580        dout = new DataOutputStream JavaDoc(this);
1581    }
1582
1583    /**
1584     * Sets block data mode to the given mode (true == on, false == off)
1585     * and returns the previous mode value. If the new mode is the same as
1586     * the old mode, no action is taken. If the new mode differs from the
1587     * old mode, any buffered data is flushed before switching to the new
1588     * mode.
1589     */

1590    boolean setBlockDataMode(boolean mode) throws IOException JavaDoc {
1591        if (blkmode == mode) {
1592        return blkmode;
1593        }
1594        drain();
1595        blkmode = mode;
1596        return !blkmode;
1597    }
1598
1599    /**
1600     * Returns true if the stream is currently in block data mode, false
1601     * otherwise.
1602     */

1603    boolean getBlockDataMode() {
1604        return blkmode;
1605    }
1606
1607    /* ----------------- generic output stream methods ----------------- */
1608    /*
1609     * The following methods are equivalent to their counterparts in
1610     * OutputStream, except that they partition written data into data
1611     * blocks when in block data mode.
1612     */

1613
1614    public void write(int b) throws IOException JavaDoc {
1615        if (pos >= MAX_BLOCK_SIZE) {
1616        drain();
1617        }
1618        buf[pos++] = (byte) b;
1619    }
1620
1621    public void write(byte[] b) throws IOException JavaDoc {
1622        write(b, 0, b.length, false);
1623    }
1624
1625    public void write(byte[] b, int off, int len) throws IOException JavaDoc {
1626        write(b, off, len, false);
1627    }
1628
1629    public void flush() throws IOException JavaDoc {
1630        drain();
1631        out.flush();
1632    }
1633
1634    public void close() throws IOException JavaDoc {
1635        flush();
1636        out.close();
1637    }
1638
1639    /**
1640     * Writes specified span of byte values from given array. If copy is
1641     * true, copies the values to an intermediate buffer before writing
1642     * them to underlying stream (to avoid exposing a reference to the
1643     * original byte array).
1644     */

1645    void write(byte[] b, int off, int len, boolean copy)
1646        throws IOException JavaDoc
1647    {
1648        if (!(copy || blkmode)) { // write directly
1649
drain();
1650        out.write(b, off, len);
1651        return;
1652        }
1653
1654        while (len > 0) {
1655        if (pos >= MAX_BLOCK_SIZE) {
1656            drain();
1657        }
1658        if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1659            // avoid unnecessary copy
1660
writeBlockHeader(MAX_BLOCK_SIZE);
1661            out.write(b, off, MAX_BLOCK_SIZE);
1662            off += MAX_BLOCK_SIZE;
1663            len -= MAX_BLOCK_SIZE;
1664        } else {
1665            int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1666            System.arraycopy(b, off, buf, pos, wlen);
1667            pos += wlen;
1668            off += wlen;
1669            len -= wlen;
1670        }
1671        }
1672    }
1673
1674    /**
1675     * Writes all buffered data from this stream to the underlying stream,
1676     * but does not flush underlying stream.
1677     */

1678    void drain() throws IOException JavaDoc {
1679        if (pos == 0) {
1680        return;
1681        }
1682        if (blkmode) {
1683        writeBlockHeader(pos);
1684        }
1685        out.write(buf, 0, pos);
1686        pos = 0;
1687    }
1688
1689    /**
1690     * Writes block data header. Data blocks shorter than 256 bytes are
1691     * prefixed with a 2-byte header; all others start with a 5-byte
1692     * header.
1693     */

1694    private void writeBlockHeader(int len) throws IOException JavaDoc {
1695        if (len <= 0xFF) {
1696        hbuf[0] = TC_BLOCKDATA;
1697        hbuf[1] = (byte) len;
1698        out.write(hbuf, 0, 2);
1699        } else {
1700        hbuf[0] = TC_BLOCKDATALONG;
1701        Bits.putInt(hbuf, 1, len);
1702        out.write(hbuf, 0, 5);
1703        }
1704    }
1705
1706
1707    /* ----------------- primitive data output methods ----------------- */
1708    /*
1709     * The following methods are equivalent to their counterparts in
1710     * DataOutputStream, except that they partition written data into data
1711     * blocks when in block data mode.
1712     */

1713
1714    public void writeBoolean(boolean v) throws IOException JavaDoc {
1715        if (pos >= MAX_BLOCK_SIZE) {
1716        drain();
1717        }
1718        Bits.putBoolean(buf, pos++, v);
1719    }
1720
1721    public void writeByte(int v) throws IOException JavaDoc {
1722        if (pos >= MAX_BLOCK_SIZE) {
1723        drain();
1724        }
1725        buf[pos++] = (byte) v;
1726    }
1727
1728    public void writeChar(int v) throws IOException JavaDoc {
1729        if (pos + 2 <= MAX_BLOCK_SIZE) {
1730        Bits.putChar(buf, pos, (char) v);
1731        pos += 2;
1732        } else {
1733        dout.writeChar(v);
1734        }
1735    }
1736
1737    public void writeShort(int v) throws IOException JavaDoc {
1738        if (pos + 2 <= MAX_BLOCK_SIZE) {
1739        Bits.putShort(buf, pos, (short) v);
1740        pos += 2;
1741        } else {
1742        dout.writeShort(v);
1743        }
1744    }
1745
1746    public void writeInt(int v) throws IOException JavaDoc {
1747        if (pos + 4 <= MAX_BLOCK_SIZE) {
1748        Bits.putInt(buf, pos, v);
1749        pos += 4;
1750        } else {
1751        dout.writeInt(v);
1752        }
1753    }
1754
1755    public void writeFloat(float v) throws IOException JavaDoc {
1756        if (pos + 4 <= MAX_BLOCK_SIZE) {
1757        Bits.putFloat(buf, pos, v);
1758        pos += 4;
1759        } else {
1760        dout.writeFloat(v);
1761        }
1762    }
1763
1764    public void writeLong(long v) throws IOException JavaDoc {
1765        if (pos + 8 <= MAX_BLOCK_SIZE) {
1766        Bits.putLong(buf, pos, v);
1767        pos += 8;
1768        } else {
1769        dout.writeLong(v);
1770        }
1771    }
1772
1773    public void writeDouble(double v) throws IOException JavaDoc {
1774        if (pos + 8 <= MAX_BLOCK_SIZE) {
1775        Bits.putDouble(buf, pos, v);
1776        pos += 8;
1777        } else {
1778        dout.writeDouble(v);
1779        }
1780    }
1781
1782    public void writeBytes(String JavaDoc s) throws IOException JavaDoc {
1783        int endoff = s.length();
1784        int cpos = 0;
1785        int csize = 0;
1786        for (int off = 0; off < endoff; ) {
1787        if (cpos >= csize) {
1788            cpos = 0;
1789            csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1790            s.getChars(off, off + csize, cbuf, 0);
1791        }
1792        if (pos >= MAX_BLOCK_SIZE) {
1793            drain();
1794        }
1795        int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1796        int stop = pos + n;
1797        while (pos < stop) {
1798            buf[pos++] = (byte) cbuf[cpos++];
1799        }
1800        off += n;
1801        }
1802    }
1803
1804    public void writeChars(String JavaDoc s) throws IOException JavaDoc {
1805        int endoff = s.length();
1806        for (int off = 0; off < endoff; ) {
1807        int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1808        s.getChars(off, off + csize, cbuf, 0);
1809        writeChars(cbuf, 0, csize);
1810        off += csize;
1811        }
1812    }
1813    
1814    public void writeUTF(String JavaDoc s) throws IOException JavaDoc {
1815        writeUTF(s, getUTFLength(s));
1816    }
1817
1818
1819    /* -------------- primitive data array output methods -------------- */
1820    /*
1821     * The following methods write out spans of primitive data values.
1822     * Though equivalent to calling the corresponding primitive write
1823     * methods repeatedly, these methods are optimized for writing groups
1824     * of primitive data values more efficiently.
1825     */

1826
1827    void writeBooleans(boolean[] v, int off, int len) throws IOException JavaDoc {
1828        int endoff = off + len;
1829        while (off < endoff) {
1830        if (pos >= MAX_BLOCK_SIZE) {
1831            drain();
1832        }
1833        int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
1834        while (off < stop) {
1835            Bits.putBoolean(buf, pos++, v[off++]);
1836        }
1837        }
1838    }
1839
1840    void writeChars(char[] v, int off, int len) throws IOException JavaDoc {
1841        int limit = MAX_BLOCK_SIZE - 2;
1842        int endoff = off + len;
1843        while (off < endoff) {
1844        if (pos <= limit) {
1845            int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1846            int stop = Math.min(endoff, off + avail);
1847            while (off < stop) {
1848            Bits.putChar(buf, pos, v[off++]);
1849            pos += 2;
1850            }
1851        } else {
1852            dout.writeChar(v[off++]);
1853        }
1854        }
1855    }
1856
1857    void writeShorts(short[] v, int off, int len) throws IOException JavaDoc {
1858        int limit = MAX_BLOCK_SIZE - 2;
1859        int endoff = off + len;
1860        while (off < endoff) {
1861        if (pos <= limit) {
1862            int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1863            int stop = Math.min(endoff, off + avail);
1864            while (off < stop) {
1865            Bits.putShort(buf, pos, v[off++]);
1866            pos += 2;
1867            }
1868        } else {
1869            dout.writeShort(v[off++]);
1870        }
1871        }
1872    }
1873
1874    void writeInts(int[] v, int off, int len) throws IOException JavaDoc {
1875        int limit = MAX_BLOCK_SIZE - 4;
1876        int endoff = off + len;
1877        while (off < endoff) {
1878        if (pos <= limit) {
1879            int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1880            int stop = Math.min(endoff, off + avail);
1881            while (off < stop) {
1882            Bits.putInt(buf, pos, v[off++]);
1883            pos += 4;
1884            }
1885        } else {
1886            dout.writeInt(v[off++]);
1887        }
1888        }
1889    }
1890
1891    void writeFloats(float[] v, int off, int len) throws IOException JavaDoc {
1892        int limit = MAX_BLOCK_SIZE - 4;
1893        int endoff = off + len;
1894        while (off < endoff) {
1895        if (pos <= limit) {
1896            int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1897            int chunklen = Math.min(endoff - off, avail);
1898            floatsToBytes(v, off, buf, pos, chunklen);
1899            off += chunklen;
1900            pos += chunklen << 2;
1901        } else {
1902            dout.writeFloat(v[off++]);
1903        }
1904        }
1905    }
1906
1907    void writeLongs(long[] v, int off, int len) throws IOException JavaDoc {
1908        int limit = MAX_BLOCK_SIZE - 8;
1909        int endoff = off + len;
1910        while (off < endoff) {
1911        if (pos <= limit) {
1912            int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1913            int stop = Math.min(endoff, off + avail);
1914            while (off < stop) {
1915            Bits.putLong(buf, pos, v[off++]);
1916            pos += 8;
1917            }
1918        } else {
1919            dout.writeLong(v[off++]);
1920        }
1921        }
1922    }
1923
1924    void writeDoubles(double[] v, int off, int len) throws IOException JavaDoc {
1925        int limit = MAX_BLOCK_SIZE - 8;
1926        int endoff = off + len;
1927        while (off < endoff) {
1928        if (pos <= limit) {
1929            int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1930            int chunklen = Math.min(endoff - off, avail);
1931            doublesToBytes(v, off, buf, pos, chunklen);
1932            off += chunklen;
1933            pos += chunklen << 3;
1934        } else {
1935            dout.writeDouble(v[off++]);
1936        }
1937        }
1938    }
1939
1940    /**
1941     * Returns the length in bytes of the UTF encoding of the given string.
1942     */

1943    long getUTFLength(String JavaDoc s) {
1944        int len = s.length();
1945        long utflen = 0;
1946        for (int off = 0; off < len; ) {
1947        int csize = Math.min(len - off, CHAR_BUF_SIZE);
1948        s.getChars(off, off + csize, cbuf, 0);
1949        for (int cpos = 0; cpos < csize; cpos++) {
1950            char c = cbuf[cpos];
1951            if (c >= 0x0001 && c <= 0x007F) {
1952            utflen++;
1953            } else if (c > 0x07FF) {
1954            utflen += 3;
1955            } else {
1956            utflen += 2;
1957            }
1958        }
1959        off += csize;
1960        }
1961        return utflen;
1962    }
1963
1964    /**
1965     * Writes the given string in UTF format. This method is used in
1966     * situations where the UTF encoding length of the string is already
1967     * known; specifying it explicitly avoids a prescan of the string to
1968     * determine its UTF length.
1969     */

1970    void writeUTF(String JavaDoc s, long utflen) throws IOException JavaDoc {
1971        if (utflen > 0xFFFFL) {
1972        throw new UTFDataFormatException JavaDoc();
1973        }
1974        writeShort((int) utflen);
1975        if (utflen == (long) s.length()) {
1976        writeBytes(s);
1977        } else {
1978        writeUTFBody(s);
1979        }
1980    }
1981
1982    /**
1983     * Writes given string in "long" UTF format. "Long" UTF format is
1984     * identical to standard UTF, except that it uses an 8 byte header
1985     * (instead of the standard 2 bytes) to convey the UTF encoding length.
1986     */

1987    void writeLongUTF(String JavaDoc s) throws IOException JavaDoc {
1988        writeLongUTF(s, getUTFLength(s));
1989    }
1990
1991    /**
1992     * Writes given string in "long" UTF format, where the UTF encoding
1993     * length of the string is already known.
1994     */

1995    void writeLongUTF(String JavaDoc s, long utflen) throws IOException JavaDoc {
1996        writeLong(utflen);
1997        if (utflen == (long) s.length()) {
1998        writeBytes(s);
1999        } else {
2000        writeUTFBody(s);
2001        }
2002    }
2003
2004    /**
2005     * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2006     * 8-byte length header) of the UTF encoding for the given string.
2007     */

2008    private void writeUTFBody(String JavaDoc s) throws IOException JavaDoc {
2009        int limit = MAX_BLOCK_SIZE - 3;
2010        int len = s.length();
2011        for (int off = 0; off < len; ) {
2012        int csize = Math.min(len - off, CHAR_BUF_SIZE);
2013        s.getChars(off, off + csize, cbuf, 0);
2014        for (int cpos = 0; cpos < csize; cpos++) {
2015            char c = cbuf[cpos];
2016            if (pos <= limit) {
2017            if (c <= 0x007F && c != 0) {
2018                buf[pos++] = (byte) c;
2019            } else if (c > 0x07FF) {
2020                buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2021                buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2022                buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2023                pos += 3;
2024            } else {
2025                buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2026                buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2027                pos += 2;
2028            }
2029            } else { // write one byte at a time to normalize block
2030
if (c <= 0x007F && c != 0) {
2031                write(c);
2032            } else if (c > 0x07FF) {
2033                write(0xE0 | ((c >> 12) & 0x0F));
2034                write(0x80 | ((c >> 6) & 0x3F));
2035                write(0x80 | ((c >> 0) & 0x3F));
2036            } else {
2037                write(0xC0 | ((c >> 6) & 0x1F));
2038                write(0x80 | ((c >> 0) & 0x3F));
2039            }
2040            }
2041        }
2042        off += csize;
2043        }
2044    }
2045    }
2046
2047    /**
2048     * Lightweight identity hash table which maps objects to integer handles,
2049     * assigned in ascending order.
2050     */

2051    private static class HandleTable {
2052
2053    /* number of mappings in table/next available handle */
2054    private int size;
2055    /* size threshold determining when to expand hash spine */
2056    private int threshold;
2057    /* factor for computing size threshold */
2058    private final float loadFactor;
2059    /* maps hash value -> candidate handle value */
2060    private int[] spine;
2061    /* maps handle value -> next candidate handle value */
2062    private int[] next;
2063    /* maps handle value -> associated object */
2064    private Object JavaDoc[] objs;
2065
2066    /**
2067     * Creates new HandleTable with given capacity and load factor.
2068     */

2069    HandleTable(int initialCapacity, float loadFactor) {
2070        this.loadFactor = loadFactor;
2071        spine = new int[initialCapacity];
2072        next = new int[initialCapacity];
2073        objs = new Object JavaDoc[initialCapacity];
2074        threshold = (int) (initialCapacity * loadFactor);
2075        clear();
2076    }
2077
2078    /**
2079     * Assigns next available handle to given object, and returns handle
2080     * value. Handles are assigned in ascending order starting at 0.
2081     */

2082    int assign(Object JavaDoc obj) {
2083        if (size >= next.length) {
2084        growEntries();
2085        }
2086        if (size >= threshold) {
2087        growSpine();
2088        }
2089        insert(obj, size);
2090        return size++;
2091    }
2092
2093    /**
2094     * Looks up and returns handle associated with given object, or -1 if
2095     * no mapping found.
2096     */

2097    int lookup(Object JavaDoc obj) {
2098        if (size == 0) {
2099        return -1;
2100        }
2101        int index = hash(obj) % spine.length;
2102        for (int i = spine[index]; i >= 0; i = next[i]) {
2103        if (objs[i] == obj) {
2104            return i;
2105        }
2106        }
2107        return -1;
2108    }
2109
2110    /**
2111     * Resets table to its initial (empty) state.
2112     */

2113    void clear() {
2114        Arrays.fill(spine, -1);
2115        Arrays.fill(objs, 0, size, null);
2116        size = 0;
2117    }
2118
2119    /**
2120     * Returns the number of mappings currently in table.
2121     */

2122    int size() {
2123        return size;
2124    }
2125
2126    /**
2127     * Inserts mapping object -> handle mapping into table. Assumes table
2128     * is large enough to accommodate new mapping.
2129     */

2130    private void insert(Object JavaDoc obj, int handle) {
2131        int index = hash(obj) % spine.length;
2132        objs[handle] = obj;
2133        next[handle] = spine[index];
2134        spine[index] = handle;
2135    }
2136
2137    /**
2138     * Expands the hash "spine" -- equivalent to increasing the number of
2139     * buckets in a conventional hash table.
2140     */

2141    private void growSpine() {
2142        spine = new int[(spine.length << 1) + 1];
2143        threshold = (int) (spine.length * loadFactor);
2144        Arrays.fill(spine, -1);
2145        for (int i = 0; i < size; i++) {
2146        insert(objs[i], i);
2147        }
2148    }
2149
2150    /**
2151     * Increases hash table capacity by lengthening entry arrays.
2152     */

2153    private void growEntries() {
2154        int newLength = (next.length << 1) + 1;
2155        int[] newNext = new int[newLength];
2156        System.arraycopy(next, 0, newNext, 0, size);
2157        next = newNext;
2158
2159        Object JavaDoc[] newObjs = new Object JavaDoc[newLength];
2160        System.arraycopy(objs, 0, newObjs, 0, size);
2161        objs = newObjs;
2162    }
2163
2164    /**
2165     * Returns hash value for given object.
2166     */

2167    private int hash(Object JavaDoc obj) {
2168        return System.identityHashCode(obj) & 0x7FFFFFFF;
2169    }
2170    }
2171
2172    /**
2173     * Lightweight identity hash table which maps objects to replacement
2174     * objects.
2175     */

2176    private static class ReplaceTable {
2177
2178    /* maps object -> index */
2179    private final HandleTable htab;
2180    /* maps index -> replacement object */
2181    private Object JavaDoc[] reps;
2182
2183    /**
2184     * Creates new ReplaceTable with given capacity and load factor.
2185     */

2186    ReplaceTable(int initialCapacity, float loadFactor) {
2187        htab = new HandleTable(initialCapacity, loadFactor);
2188        reps = new Object JavaDoc[initialCapacity];
2189    }
2190
2191    /**
2192     * Enters mapping from object to replacement object.
2193     */

2194    void assign(Object JavaDoc obj, Object JavaDoc rep) {
2195        int index = htab.assign(obj);
2196        while (index >= reps.length) {
2197        grow();
2198        }
2199        reps[index] = rep;
2200    }
2201
2202    /**
2203     * Looks up and returns replacement for given object. If no
2204     * replacement is found, returns the lookup object itself.
2205     */

2206    Object JavaDoc lookup(Object JavaDoc obj) {
2207        int index = htab.lookup(obj);
2208        return (index >= 0) ? reps[index] : obj;
2209    }
2210
2211    /**
2212     * Resets table to its initial (empty) state.
2213     */

2214    void clear() {
2215        Arrays.fill(reps, 0, htab.size(), null);
2216        htab.clear();
2217    }
2218
2219    /**
2220     * Returns the number of mappings currently in table.
2221     */

2222    int size() {
2223        return htab.size();
2224    }
2225    
2226    /**
2227     * Increases table capacity.
2228     */

2229    private void grow() {
2230        Object JavaDoc[] newReps = new Object JavaDoc[(reps.length << 1) + 1];
2231        System.arraycopy(reps, 0, newReps, 0, reps.length);
2232        reps = newReps;
2233    }
2234    }
2235}
2236
Popular Tags