1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import java.lang.reflect.Field;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.lang.reflect.Proxy;
24import java.nio.ByteOrder;
25import java.nio.charset.ModifiedUtf8;
26import java.util.List;
27import libcore.io.Memory;
28import libcore.io.SizeOf;
29
30/**
31 * A specialized {@link OutputStream} that is able to write (serialize) Java
32 * objects as well as primitive data types (int, byte, char etc.). The data can
33 * later be loaded using an ObjectInputStream.
34 *
35 * @see ObjectInputStream
36 * @see ObjectOutput
37 * @see Serializable
38 * @see Externalizable
39 */
40public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants {
41
42    private static final Class<?>[] WRITE_UNSHARED_PARAM_TYPES = new Class[] { Object.class };
43
44    /*
45     * Mask to zero SC_BLOC_DATA bit.
46     */
47    private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF);
48
49    /*
50     * How many nested levels to writeObject. We may not need this.
51     */
52    private int nestedLevels;
53
54    /*
55     * Where we write to
56     */
57    private DataOutputStream output;
58
59    /*
60     * If object replacement is enabled or not
61     */
62    private boolean enableReplace;
63
64    /*
65     * Where we write primitive types to
66     */
67    private DataOutputStream primitiveTypes;
68
69    /*
70     * Where the write primitive types are actually written to
71     */
72    private ByteArrayOutputStream primitiveTypesBuffer;
73
74    /*
75     * Table mapping Object -> Integer (handle)
76     */
77    private SerializationHandleMap objectsWritten;
78
79    /*
80     * All objects are assigned an ID (integer handle)
81     */
82    private int currentHandle;
83
84    /*
85     * Used by defaultWriteObject
86     */
87    private Object currentObject;
88
89    /*
90     * Used by defaultWriteObject
91     */
92    private ObjectStreamClass currentClass;
93
94    /*
95     * Either ObjectStreamConstants.PROTOCOL_VERSION_1 or
96     * ObjectStreamConstants.PROTOCOL_VERSION_2
97     */
98    private int protocolVersion;
99
100    /*
101     * Used to detect nested exception when saving an exception due to an error
102     */
103    private StreamCorruptedException nestedException;
104
105    /*
106     * Used to keep track of the PutField object for the class/object being
107     * written
108     */
109    private EmulatedFieldsForDumping currentPutField;
110
111    /*
112     * Allows the receiver to decide if it needs to call writeObjectOverride
113     */
114    private boolean subclassOverridingImplementation;
115
116    /*
117     * Descriptor for java.lang.reflect.Proxy
118     */
119    private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class);
120
121    /**
122     * PutField is an inner class to provide access to the persistent fields
123     * that are written to the target stream.
124     */
125    public static abstract class PutField {
126        /**
127         * Puts the value of the boolean field identified by {@code name} to the
128         * persistent field.
129         *
130         * @param name
131         *            the name of the field to serialize.
132         * @param value
133         *            the value that is put to the persistent field.
134         */
135        public abstract void put(String name, boolean value);
136
137        /**
138         * Puts the value of the character field identified by {@code name} to
139         * the persistent field.
140         *
141         * @param name
142         *            the name of the field to serialize.
143         * @param value
144         *            the value that is put to the persistent field.
145         */
146        public abstract void put(String name, char value);
147
148        /**
149         * Puts the value of the byte field identified by {@code name} to the
150         * persistent field.
151         *
152         * @param name
153         *            the name of the field to serialize.
154         * @param value
155         *            the value that is put to the persistent field.
156         */
157        public abstract void put(String name, byte value);
158
159        /**
160         * Puts the value of the short field identified by {@code name} to the
161         * persistent field.
162         *
163         * @param name
164         *            the name of the field to serialize.
165         * @param value
166         *            the value that is put to the persistent field.
167         */
168        public abstract void put(String name, short value);
169
170        /**
171         * Puts the value of the integer field identified by {@code name} to the
172         * persistent field.
173         *
174         * @param name
175         *            the name of the field to serialize.
176         * @param value
177         *            the value that is put to the persistent field.
178         */
179        public abstract void put(String name, int value);
180
181        /**
182         * Puts the value of the long field identified by {@code name} to the
183         * persistent field.
184         *
185         * @param name
186         *            the name of the field to serialize.
187         * @param value
188         *            the value that is put to the persistent field.
189         */
190        public abstract void put(String name, long value);
191
192        /**
193         * Puts the value of the float field identified by {@code name} to the
194         * persistent field.
195         *
196         * @param name
197         *            the name of the field to serialize.
198         * @param value
199         *            the value that is put to the persistent field.
200         */
201        public abstract void put(String name, float value);
202
203        /**
204         * Puts the value of the double field identified by {@code name} to the
205         * persistent field.
206         *
207         * @param name
208         *            the name of the field to serialize.
209         * @param value
210         *            the value that is put to the persistent field.
211         */
212        public abstract void put(String name, double value);
213
214        /**
215         * Puts the value of the Object field identified by {@code name} to the
216         * persistent field.
217         *
218         * @param name
219         *            the name of the field to serialize.
220         * @param value
221         *            the value that is put to the persistent field.
222         */
223        public abstract void put(String name, Object value);
224
225        /**
226         * Writes the fields to the target stream {@code out}.
227         *
228         * @param out
229         *            the target stream
230         * @throws IOException
231         *             if an error occurs while writing to the target stream.
232         * @deprecated This method is unsafe and may corrupt the target stream.
233         *             Use ObjectOutputStream#writeFields() instead.
234         */
235        @Deprecated
236        public abstract void write(ObjectOutput out) throws IOException;
237    }
238
239    /**
240     * Constructs a new {@code ObjectOutputStream}. This default constructor can
241     * be used by subclasses that do not want to use the public constructor if
242     * it allocates unneeded data.
243     *
244     * @throws IOException
245     *             if an error occurs when creating this stream.
246     */
247    protected ObjectOutputStream() throws IOException {
248        /*
249         * WARNING - we should throw IOException if not called from a subclass
250         * according to the JavaDoc. Add the test.
251         */
252        this.subclassOverridingImplementation = true;
253    }
254
255    /**
256     * Constructs a new ObjectOutputStream that writes to the OutputStream
257     * {@code output}.
258     *
259     * @param output
260     *            the non-null OutputStream to filter writes on.
261     *
262     * @throws IOException
263     *             if an error occurs while writing the object stream
264     *             header
265     */
266    public ObjectOutputStream(OutputStream output) throws IOException {
267        this.output = (output instanceof DataOutputStream) ? (DataOutputStream) output
268                : new DataOutputStream(output);
269        this.enableReplace = false;
270        this.protocolVersion = PROTOCOL_VERSION_2;
271        this.subclassOverridingImplementation = false;
272
273        resetState();
274        this.nestedException = new StreamCorruptedException();
275        // So write...() methods can be used by
276        // subclasses during writeStreamHeader()
277        primitiveTypes = this.output;
278        // Has to be done here according to the specification
279        writeStreamHeader();
280        primitiveTypes = null;
281    }
282
283    /**
284     * Writes optional information for class {@code aClass} to the output
285     * stream. This optional data can be read when deserializing the class
286     * descriptor (ObjectStreamClass) for this class from an input stream. By
287     * default, no extra data is saved.
288     *
289     * @param aClass
290     *            the class to annotate.
291     * @throws IOException
292     *             if an error occurs while writing to the target stream.
293     * @see ObjectInputStream#resolveClass(ObjectStreamClass)
294     */
295    protected void annotateClass(Class<?> aClass) throws IOException {
296        // By default no extra info is saved. Subclasses can override
297    }
298
299    /**
300     * Writes optional information for a proxy class to the target stream. This
301     * optional data can be read when deserializing the proxy class from an
302     * input stream. By default, no extra data is saved.
303     *
304     * @param aClass
305     *            the proxy class to annotate.
306     * @throws IOException
307     *             if an error occurs while writing to the target stream.
308     * @see ObjectInputStream#resolveProxyClass(String[])
309     */
310    protected void annotateProxyClass(Class<?> aClass) throws IOException {
311        // By default no extra info is saved. Subclasses can override
312    }
313
314    /**
315     * Do the necessary work to see if the receiver can be used to write
316     * primitive types like int, char, etc.
317     */
318    private void checkWritePrimitiveTypes() {
319        if (primitiveTypes == null) {
320            // If we got here we have no Stream previously created
321            // WARNING - if the stream does not grow, this code is wrong
322            primitiveTypesBuffer = new ByteArrayOutputStream(128);
323            primitiveTypes = new DataOutputStream(primitiveTypesBuffer);
324        }
325    }
326
327    /**
328     * Closes this stream. Any buffered data is flushed. This implementation
329     * closes the target stream.
330     *
331     * @throws IOException
332     *             if an error occurs while closing this stream.
333     */
334    @Override
335    public void close() throws IOException {
336        // First flush what is needed (primitive data, etc)
337        flush();
338        output.close();
339    }
340
341    /**
342     * Computes the collection of emulated fields that users can manipulate to
343     * store a representation different than the one declared by the class of
344     * the object being dumped.
345     *
346     * @see #writeFields
347     * @see #writeFieldValues(EmulatedFieldsForDumping)
348     */
349    private void computePutField() {
350        currentPutField = new EmulatedFieldsForDumping(this, currentClass);
351    }
352
353    /**
354     * Default method to write objects to this stream. Serializable fields
355     * defined in the object's class and superclasses are written to the output
356     * stream.
357     *
358     * @throws IOException
359     *             if an error occurs while writing to the target stream.
360     * @throws NotActiveException
361     *             if this method is not called from {@code writeObject()}.
362     * @see ObjectInputStream#defaultReadObject
363     */
364    public void defaultWriteObject() throws IOException {
365        if (currentObject == null) {
366            throw new NotActiveException();
367        }
368        writeFieldValues(currentObject, currentClass);
369    }
370
371    /**
372     * Writes buffered data to the target stream. This is similar to {@code
373     * flush} but the flush is not propagated to the target stream.
374     *
375     * @throws IOException
376     *             if an error occurs while writing to the target stream.
377     */
378    protected void drain() throws IOException {
379        if (primitiveTypes == null || primitiveTypesBuffer == null) {
380            return;
381        }
382
383        // If we got here we have a Stream previously created
384        int offset = 0;
385        byte[] written = primitiveTypesBuffer.toByteArray();
386        // Normalize the primitive data
387        while (offset < written.length) {
388            int toWrite = written.length - offset > 1024 ? 1024
389                    : written.length - offset;
390            if (toWrite < 256) {
391                output.writeByte(TC_BLOCKDATA);
392                output.writeByte((byte) toWrite);
393            } else {
394                output.writeByte(TC_BLOCKDATALONG);
395                output.writeInt(toWrite);
396            }
397
398            // write primitive types we had and the marker of end-of-buffer
399            output.write(written, offset, toWrite);
400            offset += toWrite;
401        }
402
403        // and now we're clean to a state where we can write an object
404        primitiveTypes = null;
405        primitiveTypesBuffer = null;
406    }
407
408    /**
409     * Dumps the parameter {@code obj} only if it is {@code null}
410     * or an object that has already been dumped previously.
411     *
412     * @param obj
413     *            Object to check if an instance previously dumped by this
414     *            stream.
415     * @return -1 if it is an instance which has not been dumped yet (and this
416     *         method does nothing). The handle if {@code obj} is an
417     *         instance which has been dumped already.
418     *
419     * @throws IOException
420     *             If an error occurs attempting to save {@code null} or
421     *             a cyclic reference.
422     */
423    private int dumpCycle(Object obj) throws IOException {
424        // If the object has been saved already, save its handle only
425        int handle = objectsWritten.get(obj);
426        if (handle != -1) {
427            writeCyclicReference(handle);
428            return handle;
429        }
430        return -1;
431    }
432
433    /**
434     * Enables object replacement for this stream. By default this is not
435     * enabled. Only trusted subclasses (loaded with system class loader) are
436     * allowed to change this status.
437     *
438     * @param enable
439     *            {@code true} to enable object replacement; {@code false} to
440     *            disable it.
441     * @return the previous setting.
442     * @see #replaceObject
443     * @see ObjectInputStream#enableResolveObject
444     */
445    protected boolean enableReplaceObject(boolean enable) {
446        boolean originalValue = enableReplace;
447        enableReplace = enable;
448        return originalValue;
449    }
450
451    /**
452     * Writes buffered data to the target stream and calls the {@code flush}
453     * method of the target stream.
454     *
455     * @throws IOException
456     *             if an error occurs while writing to or flushing the output
457     *             stream.
458     */
459    @Override
460    public void flush() throws IOException {
461        drain();
462        output.flush();
463    }
464
465    /*
466     * These methods get the value of a field named fieldName of object
467     * instance. The field is declared by declaringClass. The field is the same
468     * type as the method return value.
469     *
470     * these methods could be implemented non-natively on top of
471     * java.lang.reflect at the expense of extra object creation
472     * (java.lang.reflect.Field). Otherwise Serialization could not fetch
473     * private fields, except by the use of a native method like this one.
474     *
475     * @throws NoSuchFieldError If the field does not exist.
476     */
477    private static native Object getFieldL(Object instance, Class<?> declaringClass, String fieldName, String fieldTypeName);
478
479    /**
480     * Return the next handle to be used to indicate cyclic
481     * references being saved to the stream.
482     *
483     * @return the next handle to represent the next cyclic reference
484     */
485    private int nextHandle() {
486        return currentHandle++;
487    }
488
489    /**
490     * Gets this stream's {@code PutField} object. This object provides access
491     * to the persistent fields that are eventually written to the output
492     * stream. It is used to transfer the values from the fields of the object
493     * that is currently being written to the persistent fields.
494     *
495     * @return the PutField object from which persistent fields can be accessed
496     *         by name.
497     * @throws IOException
498     *             if an I/O error occurs.
499     * @throws NotActiveException
500     *             if this method is not called from {@code writeObject()}.
501     * @see ObjectInputStream#defaultReadObject
502     */
503    public PutField putFields() throws IOException {
504        if (currentObject == null) {
505            throw new NotActiveException();
506        }
507        if (currentPutField == null) {
508            computePutField();
509        }
510        return currentPutField;
511    }
512
513    private int registerObjectWritten(Object obj) {
514        int handle = nextHandle();
515        objectsWritten.put(obj, handle);
516        return handle;
517    }
518
519    /**
520     * Remove the unshared object from the table, and restore any previous
521     * handle.
522     *
523     * @param obj
524     *            Non-null object being dumped.
525     * @param previousHandle
526     *            The handle of the previous identical object dumped
527     */
528    private void removeUnsharedReference(Object obj, int previousHandle) {
529        if (previousHandle != -1) {
530            objectsWritten.put(obj, previousHandle);
531        } else {
532            objectsWritten.remove(obj);
533        }
534    }
535
536    /**
537     * Allows trusted subclasses to substitute the specified original {@code
538     * object} with a new object. Object substitution has to be activated first
539     * with calling {@code enableReplaceObject(true)}. This implementation just
540     * returns {@code object}.
541     *
542     * @param object
543     *            the original object for which a replacement may be defined.
544     * @return the replacement object for {@code object}.
545     * @throws IOException
546     *             if any I/O error occurs while creating the replacement
547     *             object.
548     * @see #enableReplaceObject
549     * @see ObjectInputStream#enableResolveObject
550     * @see ObjectInputStream#resolveObject
551     */
552    protected Object replaceObject(Object object) throws IOException {
553        // By default no object replacement. Subclasses can override
554        return object;
555    }
556
557    /**
558     * Resets the state of this stream. A marker is written to the stream, so
559     * that the corresponding input stream will also perform a reset at the same
560     * point. Objects previously written are no longer remembered, so they will
561     * be written again (instead of a cyclical reference) if found in the object
562     * graph.
563     *
564     * @throws IOException
565     *             if {@code reset()} is called during the serialization of an
566     *             object.
567     */
568    public void reset() throws IOException {
569        // First we flush what we have
570        drain();
571        /*
572         * And dump a reset marker, so that the ObjectInputStream can reset
573         * itself at the same point
574         */
575        output.writeByte(TC_RESET);
576        // Now we reset ourselves
577        resetState();
578    }
579
580    /**
581     * Reset the collection of objects already dumped by the receiver. If the
582     * objects are found again in the object graph, the receiver will dump them
583     * again, instead of a handle (cyclic reference).
584     *
585     */
586    private void resetSeenObjects() {
587        objectsWritten = new SerializationHandleMap();
588        currentHandle = baseWireHandle;
589    }
590
591    /**
592     * Reset the receiver. The collection of objects already dumped by the
593     * receiver is reset, and internal structures are also reset so that the
594     * receiver knows it is in a fresh clean state.
595     *
596     */
597    private void resetState() {
598        resetSeenObjects();
599        nestedLevels = 0;
600    }
601
602    /**
603     * Sets the specified protocol version to be used by this stream.
604     *
605     * @param version
606     *            the protocol version to be used. Use a {@code
607     *            PROTOCOL_VERSION_x} constant from {@code
608     *            java.io.ObjectStreamConstants}.
609     * @throws IllegalArgumentException
610     *             if an invalid {@code version} is specified.
611     * @throws IOException
612     *             if an I/O error occurs.
613     * @see ObjectStreamConstants#PROTOCOL_VERSION_1
614     * @see ObjectStreamConstants#PROTOCOL_VERSION_2
615     */
616    public void useProtocolVersion(int version) throws IOException {
617        if (!objectsWritten.isEmpty()) {
618            throw new IllegalStateException("Cannot set protocol version when stream in use");
619        }
620        if (version != ObjectStreamConstants.PROTOCOL_VERSION_1
621                && version != ObjectStreamConstants.PROTOCOL_VERSION_2) {
622            throw new IllegalArgumentException("Unknown protocol: " + version);
623        }
624        protocolVersion = version;
625    }
626
627    /**
628     * Writes {@code count} bytes from the byte array {@code buffer} starting at
629     * offset {@code index} to the target stream. Blocks until all bytes are
630     * written.
631     *
632     * @param buffer
633     *            the buffer to write.
634     * @param offset
635     *            the index of the first byte in {@code buffer} to write.
636     * @param length
637     *            the number of bytes from {@code buffer} to write to the output
638     *            stream.
639     * @throws IOException
640     *             if an error occurs while writing to the target stream.
641     */
642    @Override
643    public void write(byte[] buffer, int offset, int length) throws IOException {
644        checkWritePrimitiveTypes();
645        primitiveTypes.write(buffer, offset, length);
646    }
647
648    /**
649     * Writes a single byte to the target stream. Only the least significant
650     * byte of the integer {@code value} is written to the stream. Blocks until
651     * the byte is actually written.
652     *
653     * @param value
654     *            the byte to write.
655     * @throws IOException
656     *             if an error occurs while writing to the target stream.
657     */
658    @Override
659    public void write(int value) throws IOException {
660        checkWritePrimitiveTypes();
661        primitiveTypes.write(value);
662    }
663
664    /**
665     * Writes a boolean to the target stream.
666     *
667     * @param value
668     *            the boolean value to write to the target stream.
669     * @throws IOException
670     *             if an error occurs while writing to the target stream.
671     */
672    public void writeBoolean(boolean value) throws IOException {
673        checkWritePrimitiveTypes();
674        primitiveTypes.writeBoolean(value);
675    }
676
677    /**
678     * Writes a byte (8 bit) to the target stream.
679     *
680     * @param value
681     *            the byte to write to the target stream.
682     * @throws IOException
683     *             if an error occurs while writing to the target stream.
684     */
685    public void writeByte(int value) throws IOException {
686        checkWritePrimitiveTypes();
687        primitiveTypes.writeByte(value);
688    }
689
690    /**
691     * Writes the string {@code value} as a sequence of bytes to the target
692     * stream. Only the least significant byte of each character in the string
693     * is written.
694     *
695     * @param value
696     *            the string to write to the target stream.
697     * @throws IOException
698     *             if an error occurs while writing to the target stream.
699     */
700    public void writeBytes(String value) throws IOException {
701        checkWritePrimitiveTypes();
702        primitiveTypes.writeBytes(value);
703    }
704
705    /**
706     * Writes a character (16 bit) to the target stream.
707     *
708     * @param value
709     *            the character to write to the target stream.
710     * @throws IOException
711     *             if an error occurs while writing to the target stream.
712     */
713    public void writeChar(int value) throws IOException {
714        checkWritePrimitiveTypes();
715        primitiveTypes.writeChar(value);
716    }
717
718    /**
719     * Writes the string {@code value} as a sequence of characters to the target
720     * stream.
721     *
722     * @param value
723     *            the string to write to the target stream.
724     * @throws IOException
725     *             if an error occurs while writing to the target stream.
726     */
727    public void writeChars(String value) throws IOException {
728        checkWritePrimitiveTypes();
729        primitiveTypes.writeChars(value);
730    }
731
732    /**
733     * Write a class descriptor {@code classDesc} (an
734     * {@code ObjectStreamClass}) to the stream.
735     *
736     * @param classDesc
737     *            The class descriptor (an {@code ObjectStreamClass}) to
738     *            be dumped
739     * @param unshared
740     *            Write the object unshared
741     * @return the handle assigned to the class descriptor
742     *
743     * @throws IOException
744     *             If an IO exception happened when writing the class
745     *             descriptor.
746     */
747    private int writeClassDesc(ObjectStreamClass classDesc, boolean unshared) throws IOException {
748        if (classDesc == null) {
749            writeNull();
750            return -1;
751        }
752        int handle = -1;
753        if (!unshared) {
754            handle = dumpCycle(classDesc);
755        }
756        if (handle == -1) {
757            Class<?> classToWrite = classDesc.forClass();
758            int previousHandle = -1;
759            if (unshared) {
760                previousHandle = objectsWritten.get(classDesc);
761            }
762            // If we got here, it is a new (non-null) classDesc that will have
763            // to be registered as well
764            handle = registerObjectWritten(classDesc);
765
766            if (classDesc.isProxy()) {
767                output.writeByte(TC_PROXYCLASSDESC);
768                Class<?>[] interfaces = classToWrite.getInterfaces();
769                output.writeInt(interfaces.length);
770                for (int i = 0; i < interfaces.length; i++) {
771                    output.writeUTF(interfaces[i].getName());
772                }
773                annotateProxyClass(classToWrite);
774                output.writeByte(TC_ENDBLOCKDATA);
775                writeClassDesc(proxyClassDesc, false);
776                if (unshared) {
777                    // remove reference to unshared object
778                    removeUnsharedReference(classDesc, previousHandle);
779                }
780                return handle;
781            }
782
783            output.writeByte(TC_CLASSDESC);
784            if (protocolVersion == PROTOCOL_VERSION_1) {
785                writeNewClassDesc(classDesc);
786            } else {
787                // So write...() methods can be used by
788                // subclasses during writeClassDescriptor()
789                primitiveTypes = output;
790                writeClassDescriptor(classDesc);
791                primitiveTypes = null;
792            }
793            // Extra class info (optional)
794            annotateClass(classToWrite);
795            drain(); // flush primitive types in the annotation
796            output.writeByte(TC_ENDBLOCKDATA);
797            writeClassDesc(classDesc.getSuperclass(), unshared);
798            if (unshared) {
799                // remove reference to unshared object
800                removeUnsharedReference(classDesc, previousHandle);
801            }
802        }
803        return handle;
804    }
805
806    /**
807     * Writes a handle representing a cyclic reference (object previously
808     * dumped).
809     */
810    private void writeCyclicReference(int handle) throws IOException {
811        output.writeByte(TC_REFERENCE);
812        output.writeInt(handle);
813    }
814
815    /**
816     * Writes a double (64 bit) to the target stream.
817     *
818     * @param value
819     *            the double to write to the target stream.
820     * @throws IOException
821     *             if an error occurs while writing to the target stream.
822     */
823    public void writeDouble(double value) throws IOException {
824        checkWritePrimitiveTypes();
825        primitiveTypes.writeDouble(value);
826    }
827
828    /**
829     * Writes a collection of field descriptors (name, type name, etc) for the
830     * class descriptor {@code classDesc} (an
831     * {@code ObjectStreamClass})
832     *
833     * @param classDesc
834     *            The class descriptor (an {@code ObjectStreamClass})
835     *            for which to write field information
836     * @param externalizable
837     *            true if the descriptors are externalizable
838     *
839     * @throws IOException
840     *             If an IO exception happened when writing the field
841     *             descriptors.
842     *
843     * @see #writeObject(Object)
844     */
845    private void writeFieldDescriptors(ObjectStreamClass classDesc, boolean externalizable) throws IOException {
846        Class<?> loadedClass = classDesc.forClass();
847        ObjectStreamField[] fields = null;
848        int fieldCount = 0;
849
850        // The fields of String are not needed since Strings are treated as
851        // primitive types
852        if (!externalizable && loadedClass != ObjectStreamClass.STRINGCLASS) {
853            fields = classDesc.fields();
854            fieldCount = fields.length;
855        }
856
857        // Field count
858        output.writeShort(fieldCount);
859        // Field names
860        for (int i = 0; i < fieldCount; i++) {
861            ObjectStreamField f = fields[i];
862            boolean wasPrimitive = f.writeField(output);
863            if (!wasPrimitive) {
864                writeObject(f.getTypeString());
865            }
866        }
867    }
868
869    /**
870     * Writes the fields of the object currently being written to the target
871     * stream. The field values are buffered in the currently active {@code
872     * PutField} object, which can be accessed by calling {@code putFields()}.
873     *
874     * @throws IOException
875     *             if an error occurs while writing to the target stream.
876     * @throws NotActiveException
877     *             if there are no fields to write to the target stream.
878     * @see #putFields
879     */
880    public void writeFields() throws IOException {
881        // Has to have fields to write
882        if (currentPutField == null) {
883            throw new NotActiveException();
884        }
885        writeFieldValues(currentPutField);
886    }
887
888    /**
889     * Writes a collection of field values for the emulated fields
890     * {@code emulatedFields}
891     *
892     * @param emulatedFields
893     *            an {@code EmulatedFieldsForDumping}, concrete subclass
894     *            of {@code PutField}
895     *
896     * @throws IOException
897     *             If an IO exception happened when writing the field values.
898     *
899     * @see #writeFields
900     * @see #writeObject(Object)
901     */
902    private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) throws IOException {
903        // Access internal fields which we can set/get. Users can't do this.
904        EmulatedFields accessibleSimulatedFields = emulatedFields.emulatedFields();
905        for (EmulatedFields.ObjectSlot slot : accessibleSimulatedFields.slots()) {
906            Object fieldValue = slot.getFieldValue();
907            Class<?> type = slot.getField().getType();
908            if (type == int.class) {
909                output.writeInt(fieldValue != null ? ((Integer) fieldValue).intValue() : 0);
910            } else if (type == byte.class) {
911                output.writeByte(fieldValue != null ? ((Byte) fieldValue).byteValue() : 0);
912            } else if (type == char.class) {
913                output.writeChar(fieldValue != null ? ((Character) fieldValue).charValue() : 0);
914            } else if (type == short.class) {
915                output.writeShort(fieldValue != null ? ((Short) fieldValue).shortValue() : 0);
916            } else if (type == boolean.class) {
917                output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue).booleanValue() : false);
918            } else if (type == long.class) {
919                output.writeLong(fieldValue != null ? ((Long) fieldValue).longValue() : 0);
920            } else if (type == float.class) {
921                output.writeFloat(fieldValue != null ? ((Float) fieldValue).floatValue() : 0);
922            } else if (type == double.class) {
923                output.writeDouble(fieldValue != null ? ((Double) fieldValue).doubleValue() : 0);
924            } else {
925                // Either array or Object
926                writeObject(fieldValue);
927            }
928        }
929    }
930
931    /**
932     * Writes a collection of field values for the fields described by class
933     * descriptor {@code classDesc} (an {@code ObjectStreamClass}).
934     * This is the default mechanism, when emulated fields (an
935     * {@code PutField}) are not used. Actual values to dump are fetched
936     * directly from object {@code obj}.
937     *
938     * @param obj
939     *            Instance from which to fetch field values to dump.
940     * @param classDesc
941     *            A class descriptor (an {@code ObjectStreamClass})
942     *            defining which fields should be dumped.
943     *
944     * @throws IOException
945     *             If an IO exception happened when writing the field values.
946     *
947     * @see #writeObject(Object)
948     */
949    private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException {
950        for (ObjectStreamField fieldDesc : classDesc.fields()) {
951            try {
952                Class<?> type = fieldDesc.getTypeInternal();
953                Field field = classDesc.getReflectionField(fieldDesc);
954                if (field == null) {
955                    throw new InvalidClassException(classDesc.getName() + " doesn't have a field " + fieldDesc.getName() + " of type " + type);
956                }
957                if (type == byte.class) {
958                    output.writeByte(field.getByte(obj));
959                } else if (type == char.class) {
960                    output.writeChar(field.getChar(obj));
961                } else if (type == double.class) {
962                    output.writeDouble(field.getDouble(obj));
963                } else if (type == float.class) {
964                    output.writeFloat(field.getFloat(obj));
965                } else if (type == int.class) {
966                    output.writeInt(field.getInt(obj));
967                } else if (type == long.class) {
968                    output.writeLong(field.getLong(obj));
969                } else if (type == short.class) {
970                    output.writeShort(field.getShort(obj));
971                } else if (type == boolean.class) {
972                    output.writeBoolean(field.getBoolean(obj));
973                } else {
974                    // Reference types (including arrays).
975                    Object objField = field.get(obj);
976                    if (fieldDesc.isUnshared()) {
977                        writeUnshared(objField);
978                    } else {
979                        writeObject(objField);
980                    }
981                }
982            } catch (IllegalAccessException iae) {
983                // ObjectStreamField should have called setAccessible(true).
984                throw new AssertionError(iae);
985            } catch (NoSuchFieldError nsf) {
986                // The user defined serialPersistentFields but did not provide
987                // the glue to transfer values in writeObject, so we ended up using
988                // the default mechanism but failed to set the emulated field.
989                throw new InvalidClassException(classDesc.getName());
990            }
991        }
992    }
993
994    /**
995     * Writes a float (32 bit) to the target stream.
996     *
997     * @param value
998     *            the float to write to the target stream.
999     * @throws IOException
1000     *             if an error occurs while writing to the target stream.
1001     */
1002    public void writeFloat(float value) throws IOException {
1003        checkWritePrimitiveTypes();
1004        primitiveTypes.writeFloat(value);
1005    }
1006
1007    /**
1008     * Walks the hierarchy of classes described by class descriptor
1009     * {@code classDesc} and writes the field values corresponding to
1010     * fields declared by the corresponding class descriptor. The instance to
1011     * fetch field values from is {@code object}. If the class
1012     * (corresponding to class descriptor {@code classDesc}) defines
1013     * private instance method {@code writeObject} it will be used to
1014     * dump field values.
1015     *
1016     * @param object
1017     *            Instance from which to fetch field values to dump.
1018     * @param classDesc
1019     *            A class descriptor (an {@code ObjectStreamClass})
1020     *            defining which fields should be dumped.
1021     *
1022     * @throws IOException
1023     *             If an IO exception happened when writing the field values in
1024     *             the hierarchy.
1025     * @throws NotActiveException
1026     *             If the given object is not active
1027     *
1028     * @see #defaultWriteObject
1029     * @see #writeObject(Object)
1030     */
1031    private void writeHierarchy(Object object, ObjectStreamClass classDesc)
1032            throws IOException, NotActiveException {
1033        if (object == null) {
1034            throw new NotActiveException();
1035        }
1036
1037        // Fields are written from class closest to Object to leaf class
1038        // (down the chain)
1039        List<ObjectStreamClass> hierarchy = classDesc.getHierarchy();
1040        for (int i = 0, end = hierarchy.size(); i < end; ++i) {
1041            ObjectStreamClass osc = hierarchy.get(i);
1042            // Have to do this before calling defaultWriteObject or anything
1043            // that calls defaultWriteObject
1044            currentObject = object;
1045            currentClass = osc;
1046
1047            // See if the object has a writeObject method. If so, run it
1048            try {
1049                boolean executed = false;
1050                if (osc.hasMethodWriteObject()) {
1051                    final Method method = osc.getMethodWriteObject();
1052                    try {
1053                        method.invoke(object, new Object[] { this });
1054                        executed = true;
1055                    } catch (InvocationTargetException e) {
1056                        Throwable ex = e.getTargetException();
1057                        if (ex instanceof RuntimeException) {
1058                            throw (RuntimeException) ex;
1059                        } else if (ex instanceof Error) {
1060                            throw (Error) ex;
1061                        }
1062                        throw (IOException) ex;
1063                    } catch (IllegalAccessException e) {
1064                        throw new RuntimeException(e.toString());
1065                    }
1066                }
1067
1068                if (executed) {
1069                    drain();
1070                    output.writeByte(TC_ENDBLOCKDATA);
1071                } else {
1072                    // If the object did not have a writeMethod, call
1073                    // defaultWriteObject
1074                    defaultWriteObject();
1075                }
1076            } finally {
1077                // Cleanup, needs to run always so that we can later detect
1078                // invalid calls to defaultWriteObject
1079                currentObject = null;
1080                currentClass = null;
1081                currentPutField = null;
1082            }
1083        }
1084    }
1085
1086    /**
1087     * Writes an integer (32 bit) to the target stream.
1088     *
1089     * @param value
1090     *            the integer to write to the target stream.
1091     * @throws IOException
1092     *             if an error occurs while writing to the target stream.
1093     */
1094    public void writeInt(int value) throws IOException {
1095        checkWritePrimitiveTypes();
1096        primitiveTypes.writeInt(value);
1097    }
1098
1099    /**
1100     * Writes a long (64 bit) to the target stream.
1101     *
1102     * @param value
1103     *            the long to write to the target stream.
1104     * @throws IOException
1105     *             if an error occurs while writing to the target stream.
1106     */
1107    public void writeLong(long value) throws IOException {
1108        checkWritePrimitiveTypes();
1109        primitiveTypes.writeLong(value);
1110    }
1111
1112    /**
1113     * Write array {@code array} of class {@code arrayClass} with
1114     * component type {@code componentType} into the receiver. It is
1115     * assumed the array has not been dumped yet. Returns
1116     * the handle for this object (array) which is dumped here.
1117     *
1118     * @param array
1119     *            The array object to dump
1120     * @param arrayClass
1121     *            A {@code java.lang.Class} representing the class of the
1122     *            array
1123     * @param componentType
1124     *            A {@code java.lang.Class} representing the array
1125     *            component type
1126     * @return the handle assigned to the array
1127     *
1128     * @throws IOException
1129     *             If an IO exception happened when writing the array.
1130     */
1131    private int writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc,
1132            Class<?> componentType, boolean unshared) throws IOException {
1133        output.writeByte(TC_ARRAY);
1134        writeClassDesc(arrayClDesc, false);
1135
1136        int handle = nextHandle();
1137        if (!unshared) {
1138            objectsWritten.put(array, handle);
1139        }
1140
1141        // Now we have code duplication just because Java is typed. We have to
1142        // write N elements and assign to array positions, but we must typecast
1143        // the array first, and also call different methods depending on the
1144        // elements.
1145
1146        if (componentType.isPrimitive()) {
1147            if (componentType == int.class) {
1148                int[] intArray = (int[]) array;
1149                output.writeInt(intArray.length);
1150                for (int i = 0; i < intArray.length; i++) {
1151                    output.writeInt(intArray[i]);
1152                }
1153            } else if (componentType == byte.class) {
1154                byte[] byteArray = (byte[]) array;
1155                output.writeInt(byteArray.length);
1156                output.write(byteArray, 0, byteArray.length);
1157            } else if (componentType == char.class) {
1158                char[] charArray = (char[]) array;
1159                output.writeInt(charArray.length);
1160                for (int i = 0; i < charArray.length; i++) {
1161                    output.writeChar(charArray[i]);
1162                }
1163            } else if (componentType == short.class) {
1164                short[] shortArray = (short[]) array;
1165                output.writeInt(shortArray.length);
1166                for (int i = 0; i < shortArray.length; i++) {
1167                    output.writeShort(shortArray[i]);
1168                }
1169            } else if (componentType == boolean.class) {
1170                boolean[] booleanArray = (boolean[]) array;
1171                output.writeInt(booleanArray.length);
1172                for (int i = 0; i < booleanArray.length; i++) {
1173                    output.writeBoolean(booleanArray[i]);
1174                }
1175            } else if (componentType == long.class) {
1176                long[] longArray = (long[]) array;
1177                output.writeInt(longArray.length);
1178                for (int i = 0; i < longArray.length; i++) {
1179                    output.writeLong(longArray[i]);
1180                }
1181            } else if (componentType == float.class) {
1182                float[] floatArray = (float[]) array;
1183                output.writeInt(floatArray.length);
1184                for (int i = 0; i < floatArray.length; i++) {
1185                    output.writeFloat(floatArray[i]);
1186                }
1187            } else if (componentType == double.class) {
1188                double[] doubleArray = (double[]) array;
1189                output.writeInt(doubleArray.length);
1190                for (int i = 0; i < doubleArray.length; i++) {
1191                    output.writeDouble(doubleArray[i]);
1192                }
1193            } else {
1194                throw new InvalidClassException("Wrong base type in " + arrayClass.getName());
1195            }
1196        } else {
1197            // Array of Objects
1198            Object[] objectArray = (Object[]) array;
1199            output.writeInt(objectArray.length);
1200            for (int i = 0; i < objectArray.length; i++) {
1201                // TODO: This place is the opportunity for enhancement
1202                //      We can implement writing elements through fast-path,
1203                //      without setting up the context (see writeObject()) for
1204                //      each element with public API
1205                writeObject(objectArray[i]);
1206            }
1207        }
1208        return handle;
1209    }
1210
1211    /**
1212     * Write class {@code object} into the receiver. It is assumed the
1213     * class has not been dumped yet. Classes are not really dumped, but a class
1214     * descriptor ({@code ObjectStreamClass}) that corresponds to them.
1215     * Returns the handle for this object (class) which is dumped here.
1216     *
1217     * @param object
1218     *            The {@code java.lang.Class} object to dump
1219     * @return the handle assigned to the class being dumped
1220     *
1221     * @throws IOException
1222     *             If an IO exception happened when writing the class.
1223     */
1224    private int writeNewClass(Class<?> object, boolean unshared) throws IOException {
1225        output.writeByte(TC_CLASS);
1226
1227        // Instances of java.lang.Class are always Serializable, even if their
1228        // instances aren't (e.g. java.lang.Object.class).
1229        // We cannot call lookup because it returns null if the parameter
1230        // represents instances that cannot be serialized, and that is not what
1231        // we want.
1232        ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object);
1233
1234        // The handle for the classDesc is NOT the handle for the class object
1235        // being dumped. We must allocate a new handle and return it.
1236        if (clDesc.isEnum()) {
1237            writeEnumDesc(object, clDesc, unshared);
1238        } else {
1239            writeClassDesc(clDesc, unshared);
1240        }
1241
1242        int handle = nextHandle();
1243        if (!unshared) {
1244            objectsWritten.put(object, handle);
1245        }
1246
1247        return handle;
1248    }
1249
1250    /**
1251     * Write class descriptor {@code classDesc} into the receiver. It is
1252     * assumed the class descriptor has not been dumped yet. The class
1253     * descriptors for the superclass chain will be dumped as well. Returns
1254     * the handle for this object (class descriptor) which is dumped here.
1255     *
1256     * @param classDesc
1257     *            The {@code ObjectStreamClass} object to dump
1258     *
1259     * @throws IOException
1260     *             If an IO exception happened when writing the class
1261     *             descriptor.
1262     */
1263    private void writeNewClassDesc(ObjectStreamClass classDesc)
1264            throws IOException {
1265        output.writeUTF(classDesc.getName());
1266        output.writeLong(classDesc.getSerialVersionUID());
1267        byte flags = classDesc.getFlags();
1268
1269        boolean externalizable = classDesc.isExternalizable();
1270
1271        if (externalizable) {
1272            if (protocolVersion == PROTOCOL_VERSION_1) {
1273                flags &= NOT_SC_BLOCK_DATA;
1274            } else {
1275                // Change for 1.2. Objects can be saved in old format
1276                // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2).
1277                flags |= SC_BLOCK_DATA;
1278            }
1279        }
1280        output.writeByte(flags);
1281        if ((SC_ENUM | SC_SERIALIZABLE) != classDesc.getFlags()) {
1282            writeFieldDescriptors(classDesc, externalizable);
1283        } else {
1284            // enum write no fields
1285            output.writeShort(0);
1286        }
1287    }
1288
1289    /**
1290     * Writes a class descriptor to the target stream.
1291     *
1292     * @param classDesc
1293     *            the class descriptor to write to the target stream.
1294     * @throws IOException
1295     *             if an error occurs while writing to the target stream.
1296     */
1297    protected void writeClassDescriptor(ObjectStreamClass classDesc)
1298            throws IOException {
1299        writeNewClassDesc(classDesc);
1300    }
1301
1302    /**
1303     * Write exception {@code ex} into the receiver. It is assumed the
1304     * exception has not been dumped yet. Returns
1305     * the handle for this object (exception) which is dumped here.
1306     * This is used to dump the exception instance that happened (if any) when
1307     * dumping the original object graph. The set of seen objects will be reset
1308     * just before and just after dumping this exception object.
1309     *
1310     * When exceptions are found normally in the object graph, they are dumped
1311     * as a regular object, and not by this method. In that case, the set of
1312     * "known objects" is not reset.
1313     *
1314     * @param ex
1315     *            Exception object to dump
1316     *
1317     * @throws IOException
1318     *             If an IO exception happened when writing the exception
1319     *             object.
1320     */
1321    private void writeNewException(Exception ex) throws IOException {
1322        output.writeByte(TC_EXCEPTION);
1323        resetSeenObjects();
1324        writeObjectInternal(ex, false, false, false); // No replacements
1325        resetSeenObjects();
1326    }
1327
1328    /**
1329     * Write object {@code object} of class {@code theClass} into
1330     * the receiver. It is assumed the object has not been dumped yet.
1331     * Return the handle for this object which
1332     * is dumped here.
1333     *
1334     * If the object implements {@code Externalizable} its
1335     * {@code writeExternal} is called. Otherwise, all fields described
1336     * by the class hierarchy is dumped. Each class can define how its declared
1337     * instance fields are dumped by defining a private method
1338     * {@code writeObject}
1339     *
1340     * @param object
1341     *            The object to dump
1342     * @param theClass
1343     *            A {@code java.lang.Class} representing the class of the
1344     *            object
1345     * @param unshared
1346     *            Write the object unshared
1347     * @return the handle assigned to the object
1348     *
1349     * @throws IOException
1350     *             If an IO exception happened when writing the object.
1351     */
1352    private int writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc,
1353            boolean unshared) throws IOException {
1354        // Not String, not null, not array, not cyclic reference
1355
1356        EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
1357        currentPutField = null; // null it, to make sure one will be computed if
1358        // needed
1359
1360        boolean externalizable = clDesc.isExternalizable();
1361        boolean serializable = clDesc.isSerializable();
1362        if (!externalizable && !serializable) {
1363            // Object is neither externalizable nor serializable. Error
1364            throw new NotSerializableException(theClass.getName());
1365        }
1366
1367        // Either serializable or externalizable, now we can save info
1368        output.writeByte(TC_OBJECT);
1369        writeClassDesc(clDesc, false);
1370        int previousHandle = -1;
1371        if (unshared) {
1372            previousHandle = objectsWritten.get(object);
1373        }
1374
1375        int handle = registerObjectWritten(object);
1376
1377        // This is how we know what to do in defaultWriteObject. And it is also
1378        // used by defaultWriteObject to check if it was called from an invalid
1379        // place.
1380        // It allows writeExternal to call defaultWriteObject and have it work.
1381        currentObject = object;
1382        currentClass = clDesc;
1383        try {
1384            if (externalizable) {
1385                boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1;
1386                if (noBlockData) {
1387                    primitiveTypes = output;
1388                }
1389                // Object is externalizable, just call its own method
1390                ((Externalizable) object).writeExternal(this);
1391                if (noBlockData) {
1392                    primitiveTypes = null;
1393                } else {
1394                    // Similar to the code in writeHierarchy when object
1395                    // implements writeObject.
1396                    // Any primitive data has to be flushed and a tag must be
1397                    // written
1398                    drain();
1399                    output.writeByte(TC_ENDBLOCKDATA);
1400                }
1401            } else { // If it got here, it has to be Serializable
1402                // Object is serializable. Walk the class chain writing the
1403                // fields
1404                writeHierarchy(object, currentClass);
1405            }
1406        } finally {
1407            // Cleanup, needs to run always so that we can later detect invalid
1408            // calls to defaultWriteObject
1409            if (unshared) {
1410                // remove reference to unshared object
1411                removeUnsharedReference(object, previousHandle);
1412            }
1413            currentObject = null;
1414            currentClass = null;
1415            currentPutField = originalCurrentPutField;
1416        }
1417
1418        return handle;
1419    }
1420
1421    /**
1422     * Write String {@code object} into the receiver. It is assumed the
1423     * String has not been dumped yet. Returns the handle for this object (String) which is dumped here.
1424     * Strings are saved encoded with {@link DataInput modified UTF-8}.
1425     *
1426     * @param object
1427     *            the string to dump.
1428     * @return the handle assigned to the String being dumped
1429     *
1430     * @throws IOException
1431     *             If an IO exception happened when writing the String.
1432     */
1433    private int writeNewString(String object, boolean unshared) throws IOException {
1434        long count = ModifiedUtf8.countBytes(object, false);
1435        byte[] buffer;
1436        int offset = 0;
1437        if (count <= 0xffff) {
1438            buffer = new byte[1 + SizeOf.SHORT + (int) count];
1439            buffer[offset++] = TC_STRING;
1440            Memory.pokeShort(buffer, offset, (short) count, ByteOrder.BIG_ENDIAN);
1441            offset += SizeOf.SHORT;
1442        } else {
1443            buffer = new byte[1 + SizeOf.LONG + (int) count];
1444            buffer[offset++] = TC_LONGSTRING;
1445            Memory.pokeLong(buffer, offset, count, ByteOrder.BIG_ENDIAN);
1446            offset += SizeOf.LONG;
1447        }
1448        ModifiedUtf8.encode(buffer, offset, object);
1449        output.write(buffer, 0, buffer.length);
1450
1451        int handle = nextHandle();
1452        if (!unshared) {
1453            objectsWritten.put(object, handle);
1454        }
1455
1456        return handle;
1457    }
1458
1459    /**
1460     * Write a special tag that indicates the value {@code null} into the
1461     * receiver.
1462     *
1463     * @throws IOException
1464     *             If an IO exception happened when writing the tag for
1465     *             {@code null}.
1466     */
1467    private void writeNull() throws IOException {
1468        output.writeByte(TC_NULL);
1469    }
1470
1471    /**
1472     * Writes an object to the target stream.
1473     *
1474     * @param object
1475     *            the object to write to the target stream.
1476     * @throws IOException
1477     *             if an error occurs while writing to the target stream.
1478     * @see ObjectInputStream#readObject()
1479     */
1480    public final void writeObject(Object object) throws IOException {
1481        writeObject(object, false);
1482    }
1483
1484    /**
1485     * Writes an unshared object to the target stream. This method is identical
1486     * to {@code writeObject}, except that it always writes a new object to the
1487     * stream versus the use of back-referencing for identical objects by
1488     * {@code writeObject}.
1489     *
1490     * @param object
1491     *            the object to write to the target stream.
1492     * @throws IOException
1493     *             if an error occurs while writing to the target stream.
1494     * @see ObjectInputStream#readUnshared()
1495     */
1496    public void writeUnshared(Object object) throws IOException {
1497        writeObject(object, true);
1498    }
1499
1500    private void writeObject(Object object, boolean unshared) throws IOException {
1501        boolean setOutput = (primitiveTypes == output);
1502        if (setOutput) {
1503            primitiveTypes = null;
1504        }
1505        // This is the specified behavior in JDK 1.2. Very bizarre way to allow
1506        // behavior overriding.
1507        if (subclassOverridingImplementation && !unshared) {
1508            writeObjectOverride(object);
1509            return;
1510        }
1511
1512        try {
1513            // First we need to flush primitive types if they were written
1514            drain();
1515            // Actual work, and class-based replacement should be computed
1516            // if needed.
1517            writeObjectInternal(object, unshared, true, true);
1518            if (setOutput) {
1519                primitiveTypes = output;
1520            }
1521        } catch (IOException ioEx1) {
1522            // This will make it pass through until the top caller. It also
1523            // lets it pass through the nested exception.
1524            if (nestedLevels == 0 && ioEx1 != nestedException) {
1525                try {
1526                    writeNewException(ioEx1);
1527                } catch (IOException ioEx2) {
1528                    nestedException.fillInStackTrace();
1529                    throw nestedException;
1530                }
1531            }
1532            throw ioEx1; // and then we propagate the original exception
1533        }
1534    }
1535
1536    /**
1537     * Write object {@code object} into the receiver's underlying stream.
1538     *
1539     * @param object
1540     *            The object to write
1541     * @param unshared
1542     *            Write the object unshared
1543     * @param computeClassBasedReplacement
1544     *            A boolean indicating if class-based replacement should be
1545     *            computed (if supported) for the object.
1546     * @param computeStreamReplacement
1547     *            A boolean indicating if stream-based replacement should be
1548     *            computed (if supported) for the object.
1549     * @return the handle assigned to the final object being dumped
1550     *
1551     * @throws IOException
1552     *             If an IO exception happened when writing the object
1553     *
1554     * @see ObjectInputStream#readObject()
1555     */
1556    private int writeObjectInternal(Object object, boolean unshared,
1557            boolean computeClassBasedReplacement,
1558            boolean computeStreamReplacement) throws IOException {
1559
1560        if (object == null) {
1561            writeNull();
1562            return -1;
1563        }
1564        int handle = -1;
1565        if (!unshared) {
1566            handle = dumpCycle(object);
1567            if (handle != -1) {
1568                return handle; // cyclic reference
1569            }
1570        }
1571
1572        // Non-null object, first time seen...
1573        Class<?> objClass = object.getClass();
1574        ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass);
1575
1576        nestedLevels++;
1577        try {
1578
1579            if (!(enableReplace && computeStreamReplacement)) {
1580                // Is it a Class ?
1581                if (objClass == ObjectStreamClass.CLASSCLASS) {
1582                    return writeNewClass((Class<?>) object, unshared);
1583                }
1584                // Is it an ObjectStreamClass ?
1585                if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1586                    return writeClassDesc((ObjectStreamClass) object, unshared);
1587                }
1588            }
1589
1590            if (clDesc.isSerializable() && computeClassBasedReplacement) {
1591                if (clDesc.hasMethodWriteReplace()){
1592                    Method methodWriteReplace = clDesc.getMethodWriteReplace();
1593                    Object replObj = null;
1594                    try {
1595                        replObj = methodWriteReplace.invoke(object, (Object[]) null);
1596                    } catch (IllegalAccessException iae) {
1597                        replObj = object;
1598                    } catch (InvocationTargetException ite) {
1599                        // WARNING - Not sure this is the right thing to do
1600                        // if we can't run the method
1601                        Throwable target = ite.getTargetException();
1602                        if (target instanceof ObjectStreamException) {
1603                            throw (ObjectStreamException) target;
1604                        } else if (target instanceof Error) {
1605                            throw (Error) target;
1606                        } else {
1607                            throw (RuntimeException) target;
1608                        }
1609                    }
1610                    if (replObj != object) {
1611                        // All over, class-based replacement off this time.
1612                        int replacementHandle = writeObjectInternal(replObj, false, false,
1613                                computeStreamReplacement);
1614                        // Make the original object also map to the same
1615                        // handle.
1616                        if (replacementHandle != -1) {
1617                            objectsWritten.put(object, replacementHandle);
1618                        }
1619                        return replacementHandle;
1620                    }
1621                }
1622
1623            }
1624
1625            // We get here either if class-based replacement was not needed or
1626            // if it was needed but produced the same object or if it could not
1627            // be computed.
1628            if (enableReplace && computeStreamReplacement) {
1629                // Now we compute the stream-defined replacement.
1630                Object streamReplacement = replaceObject(object);
1631                if (streamReplacement != object) {
1632                    // All over, class-based replacement off this time.
1633                    int replacementHandle = writeObjectInternal(streamReplacement, false,
1634                            computeClassBasedReplacement, false);
1635                    // Make the original object also map to the same handle.
1636                    if (replacementHandle != -1) {
1637                        objectsWritten.put(object, replacementHandle);
1638                    }
1639                    return replacementHandle;
1640                }
1641            }
1642
1643            // We get here if stream-based replacement produced the same object
1644
1645            // Is it a Class ?
1646            if (objClass == ObjectStreamClass.CLASSCLASS) {
1647                return writeNewClass((Class<?>) object, unshared);
1648            }
1649
1650            // Is it an ObjectStreamClass ?
1651            if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1652                return writeClassDesc((ObjectStreamClass) object, unshared);
1653            }
1654
1655            // Is it a String ? (instanceof, but == is faster)
1656            if (objClass == ObjectStreamClass.STRINGCLASS) {
1657                return writeNewString((String) object, unshared);
1658            }
1659
1660            // Is it an Array ?
1661            if (objClass.isArray()) {
1662                return writeNewArray(object, objClass, clDesc, objClass
1663                        .getComponentType(), unshared);
1664            }
1665
1666            if (object instanceof Enum) {
1667                return writeNewEnum(object, objClass, unshared);
1668            }
1669
1670            // Not a String or Class or Array. Default procedure.
1671            return writeNewObject(object, objClass, clDesc, unshared);
1672        } finally {
1673            nestedLevels--;
1674        }
1675    }
1676
1677    // write for Enum Class Desc only, which is different from other classes
1678    private ObjectStreamClass writeEnumDesc(Class<?> theClass, ObjectStreamClass classDesc, boolean unshared)
1679            throws IOException {
1680        // write classDesc, classDesc for enum is different
1681
1682        // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM)
1683        classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
1684        int previousHandle = -1;
1685        if (unshared) {
1686            previousHandle = objectsWritten.get(classDesc);
1687        }
1688        int handle = -1;
1689        if (!unshared) {
1690            handle = dumpCycle(classDesc);
1691        }
1692        if (handle == -1) {
1693            Class<?> classToWrite = classDesc.forClass();
1694            // If we got here, it is a new (non-null) classDesc that will have
1695            // to be registered as well
1696            registerObjectWritten(classDesc);
1697
1698            output.writeByte(TC_CLASSDESC);
1699            if (protocolVersion == PROTOCOL_VERSION_1) {
1700                writeNewClassDesc(classDesc);
1701            } else {
1702                // So write...() methods can be used by
1703                // subclasses during writeClassDescriptor()
1704                primitiveTypes = output;
1705                writeClassDescriptor(classDesc);
1706                primitiveTypes = null;
1707            }
1708            // Extra class info (optional)
1709            annotateClass(classToWrite);
1710            drain(); // flush primitive types in the annotation
1711            output.writeByte(TC_ENDBLOCKDATA);
1712            // write super class
1713            ObjectStreamClass superClassDesc = classDesc.getSuperclass();
1714            if (superClassDesc != null) {
1715                // super class is also enum
1716                superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
1717                writeEnumDesc(superClassDesc.forClass(), superClassDesc, unshared);
1718            } else {
1719                output.writeByte(TC_NULL);
1720            }
1721            if (unshared) {
1722                // remove reference to unshared object
1723                removeUnsharedReference(classDesc, previousHandle);
1724            }
1725        }
1726        return classDesc;
1727    }
1728
1729    private int writeNewEnum(Object object, Class<?> theClass, boolean unshared) throws IOException {
1730        // write new Enum
1731        EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
1732        // null it, to make sure one will be computed if needed
1733        currentPutField = null;
1734
1735        output.writeByte(TC_ENUM);
1736        while (theClass != null && !theClass.isEnum()) {
1737            // write enum only
1738            theClass = theClass.getSuperclass();
1739        }
1740        ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass);
1741        writeEnumDesc(theClass, classDesc, unshared);
1742
1743        int previousHandle = -1;
1744        if (unshared) {
1745            previousHandle = objectsWritten.get(object);
1746        }
1747        int handle = registerObjectWritten(object);
1748
1749        ObjectStreamField[] fields = classDesc.getSuperclass().fields();
1750        // Only write field "name" for enum class, which is the second field of
1751        // enum, that is fields[1]. Ignore all non-fields and fields.length < 2
1752        if (fields != null && fields.length > 1) {
1753            Field field = classDesc.getSuperclass().getReflectionField(fields[1]);
1754            if (field == null) {
1755                throw new NoSuchFieldError();
1756            }
1757            try {
1758                String str = (String) field.get(object);
1759                int strHandle = -1;
1760                if (!unshared) {
1761                    strHandle = dumpCycle(str);
1762                }
1763                if (strHandle == -1) {
1764                    writeNewString(str, unshared);
1765                }
1766            } catch (IllegalAccessException iae) {
1767                throw new AssertionError(iae);
1768            }
1769        }
1770
1771        if (unshared) {
1772            // remove reference to unshared object
1773            removeUnsharedReference(object, previousHandle);
1774        }
1775        currentPutField = originalCurrentPutField;
1776        return handle;
1777    }
1778
1779    /**
1780     * Method to be overridden by subclasses to write {@code object} to the
1781     * target stream.
1782     *
1783     * @param object
1784     *            the object to write to the target stream.
1785     * @throws IOException
1786     *             if an error occurs while writing to the target stream.
1787     */
1788    protected void writeObjectOverride(Object object) throws IOException {
1789        if (!subclassOverridingImplementation) {
1790            // Subclasses must override.
1791            throw new IOException();
1792        }
1793    }
1794
1795    /**
1796     * Writes a short (16 bit) to the target stream.
1797     *
1798     * @param value
1799     *            the short to write to the target stream.
1800     * @throws IOException
1801     *             if an error occurs while writing to the target stream.
1802     */
1803    public void writeShort(int value) throws IOException {
1804        checkWritePrimitiveTypes();
1805        primitiveTypes.writeShort(value);
1806    }
1807
1808    /**
1809     * Writes the {@link ObjectOutputStream} header to the target stream.
1810     *
1811     * @throws IOException
1812     *             if an error occurs while writing to the target stream.
1813     */
1814    protected void writeStreamHeader() throws IOException {
1815        output.writeShort(STREAM_MAGIC);
1816        output.writeShort(STREAM_VERSION);
1817    }
1818
1819    /**
1820     * Writes a string encoded with {@link DataInput modified UTF-8} to the
1821     * target stream.
1822     *
1823     * @param value
1824     *            the string to write to the target stream.
1825     * @throws IOException
1826     *             if an error occurs while writing to the target stream.
1827     */
1828    public void writeUTF(String value) throws IOException {
1829        checkWritePrimitiveTypes();
1830        primitiveTypes.writeUTF(value);
1831    }
1832}
1833