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