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