ObjectInputStream.java revision 3d93b50c1e9d8cb12d17a4331edd27ee2a1dc7a9
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 dalvik.system.VMStack;
21import java.io.EmulatedFields.ObjectSlot;
22import java.lang.reflect.Array;
23import java.lang.reflect.Field;
24import java.lang.reflect.InvocationTargetException;
25import java.lang.reflect.Method;
26import java.lang.reflect.Proxy;
27import java.security.PrivilegedAction;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.List;
32import libcore.util.EmptyArray;
33
34/**
35 * A specialized {@link InputStream} that is able to read (deserialize) Java
36 * objects as well as primitive data types (int, byte, char etc.). The data has
37 * typically been saved using an ObjectOutputStream.
38 *
39 * @see ObjectOutputStream
40 * @see ObjectInput
41 * @see Serializable
42 * @see Externalizable
43 */
44public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants {
45
46    // TODO: this is non-static to avoid sync contention. Would static be faster?
47    private InputStream emptyStream = new ByteArrayInputStream(EmptyArray.BYTE);
48
49    // To put into objectsRead when reading unsharedObject
50    private static final Object UNSHARED_OBJ = new Object(); // $NON-LOCK-1$
51
52    // If the receiver has already read & not consumed a TC code
53    private boolean hasPushbackTC;
54
55    // Push back TC code if the variable above is true
56    private byte pushbackTC;
57
58    // How many nested levels to readObject. When we reach 0 we have to validate
59    // the graph then reset it
60    private int nestedLevels;
61
62    // All objects are assigned an ID (integer handle)
63    private int nextHandle;
64
65    // Where we read from
66    private DataInputStream input;
67
68    // Where we read primitive types from
69    private DataInputStream primitiveTypes;
70
71    // Where we keep primitive type data
72    private InputStream primitiveData = emptyStream;
73
74    // Resolve object is a mechanism for replacement
75    private boolean enableResolve;
76
77    /**
78     * All the objects we've read, indexed by their serialization handle (minus the base offset).
79     */
80    private ArrayList<Object> objectsRead;
81
82    // Used by defaultReadObject
83    private Object currentObject;
84
85    // Used by defaultReadObject
86    private ObjectStreamClass currentClass;
87
88    // All validations to be executed when the complete graph is read. See inner
89    // type below.
90    private InputValidationDesc[] validations;
91
92    // Allows the receiver to decide if it needs to call readObjectOverride
93    private boolean subclassOverridingImplementation;
94
95    // Original caller's class loader, used to perform class lookups
96    private ClassLoader callerClassLoader;
97
98    // false when reading missing fields
99    private boolean mustResolve = true;
100
101    // Handle for the current class descriptor
102    private int descriptorHandle = -1;
103
104    private static final HashMap<String, Class<?>> PRIMITIVE_CLASSES = new HashMap<String, Class<?>>();
105    static {
106        PRIMITIVE_CLASSES.put("boolean", boolean.class);
107        PRIMITIVE_CLASSES.put("byte", byte.class);
108        PRIMITIVE_CLASSES.put("char", char.class);
109        PRIMITIVE_CLASSES.put("double", double.class);
110        PRIMITIVE_CLASSES.put("float", float.class);
111        PRIMITIVE_CLASSES.put("int", int.class);
112        PRIMITIVE_CLASSES.put("long", long.class);
113        PRIMITIVE_CLASSES.put("short", short.class);
114        PRIMITIVE_CLASSES.put("void", void.class);
115    }
116
117    // Internal type used to keep track of validators & corresponding priority
118    static class InputValidationDesc {
119        ObjectInputValidation validator;
120
121        int priority;
122    }
123
124    /**
125     * GetField is an inner class that provides access to the persistent fields
126     * read from the source stream.
127     */
128    public abstract static class GetField {
129        /**
130         * Gets the ObjectStreamClass that describes a field.
131         *
132         * @return the descriptor class for a serialized field.
133         */
134        public abstract ObjectStreamClass getObjectStreamClass();
135
136        /**
137         * Indicates if the field identified by {@code name} is defaulted. This
138         * means that it has no value in this stream.
139         *
140         * @param name
141         *            the name of the field to check.
142         * @return {@code true} if the field is defaulted, {@code false}
143         *         otherwise.
144         * @throws IllegalArgumentException
145         *             if {@code name} does not identify a serializable field.
146         * @throws IOException
147         *             if an error occurs while reading from the source input
148         *             stream.
149         */
150        public abstract boolean defaulted(String name) throws IOException,
151                IllegalArgumentException;
152
153        /**
154         * Gets the value of the boolean field identified by {@code name} from
155         * the persistent field.
156         *
157         * @param name
158         *            the name of the field to get.
159         * @param defaultValue
160         *            the default value that is used if the field does not have
161         *            a value when read from the source stream.
162         * @return the value of the field identified by {@code name}.
163         * @throws IOException
164         *             if an error occurs while reading from the source input
165         *             stream.
166         * @throws IllegalArgumentException
167         *             if the type of the field identified by {@code name} is
168         *             not {@code boolean}.
169         */
170        public abstract boolean get(String name, boolean defaultValue)
171                throws IOException, IllegalArgumentException;
172
173        /**
174         * Gets the value of the character field identified by {@code name} from
175         * the persistent field.
176         *
177         * @param name
178         *            the name of the field to get.
179         * @param defaultValue
180         *            the default value that is used if the field does not have
181         *            a value when read from the source stream.
182         * @return the value of the field identified by {@code name}.
183         * @throws IOException
184         *             if an error occurs while reading from the source input
185         *             stream.
186         * @throws IllegalArgumentException
187         *             if the type of the field identified by {@code name} is
188         *             not {@code char}.
189         */
190        public abstract char get(String name, char defaultValue)
191                throws IOException, IllegalArgumentException;
192
193        /**
194         * Gets the value of the byte field identified by {@code name} from the
195         * persistent field.
196         *
197         * @param name
198         *            the name of the field to get.
199         * @param defaultValue
200         *            the default value that is used if the field does not have
201         *            a value when read from the source stream.
202         * @return the value of the field identified by {@code name}.
203         * @throws IOException
204         *             if an error occurs while reading from the source input
205         *             stream.
206         * @throws IllegalArgumentException
207         *             if the type of the field identified by {@code name} is
208         *             not {@code byte}.
209         */
210        public abstract byte get(String name, byte defaultValue)
211                throws IOException, IllegalArgumentException;
212
213        /**
214         * Gets the value of the short field identified by {@code name} from the
215         * persistent field.
216         *
217         * @param name
218         *            the name of the field to get.
219         * @param defaultValue
220         *            the default value that is used if the field does not have
221         *            a value when read from the source stream.
222         * @return the value of the field identified by {@code name}.
223         * @throws IOException
224         *             if an error occurs while reading from the source input
225         *             stream.
226         * @throws IllegalArgumentException
227         *             if the type of the field identified by {@code name} is
228         *             not {@code short}.
229         */
230        public abstract short get(String name, short defaultValue)
231                throws IOException, IllegalArgumentException;
232
233        /**
234         * Gets the value of the integer field identified by {@code name} from
235         * the persistent field.
236         *
237         * @param name
238         *            the name of the field to get.
239         * @param defaultValue
240         *            the default value that is used if the field does not have
241         *            a value when read from the source stream.
242         * @return the value of the field identified by {@code name}.
243         * @throws IOException
244         *             if an error occurs while reading from the source input
245         *             stream.
246         * @throws IllegalArgumentException
247         *             if the type of the field identified by {@code name} is
248         *             not {@code int}.
249         */
250        public abstract int get(String name, int defaultValue)
251                throws IOException, IllegalArgumentException;
252
253        /**
254         * Gets the value of the long field identified by {@code name} from the
255         * persistent field.
256         *
257         * @param name
258         *            the name of the field to get.
259         * @param defaultValue
260         *            the default value that is used if the field does not have
261         *            a value when read from the source stream.
262         * @return the value of the field identified by {@code name}.
263         * @throws IOException
264         *             if an error occurs while reading from the source input
265         *             stream.
266         * @throws IllegalArgumentException
267         *             if the type of the field identified by {@code name} is
268         *             not {@code long}.
269         */
270        public abstract long get(String name, long defaultValue)
271                throws IOException, IllegalArgumentException;
272
273        /**
274         * Gets the value of the float field identified by {@code name} from the
275         * persistent field.
276         *
277         * @param name
278         *            the name of the field to get.
279         * @param defaultValue
280         *            the default value that is used if the field does not have
281         *            a value when read from the source stream.
282         * @return the value of the field identified by {@code name}.
283         * @throws IOException
284         *             if an error occurs while reading from the source input
285         *             stream.
286         * @throws IllegalArgumentException
287         *             if the type of the field identified by {@code float} is
288         *             not {@code char}.
289         */
290        public abstract float get(String name, float defaultValue)
291                throws IOException, IllegalArgumentException;
292
293        /**
294         * Gets the value of the double field identified by {@code name} from
295         * the persistent field.
296         *
297         * @param name
298         *            the name of the field to get.
299         * @param defaultValue
300         *            the default value that is used if the field does not have
301         *            a value when read from the source stream.
302         * @return the value of the field identified by {@code name}.
303         * @throws IOException
304         *             if an error occurs while reading from the source input
305         *             stream.
306         * @throws IllegalArgumentException
307         *             if the type of the field identified by {@code name} is
308         *             not {@code double}.
309         */
310        public abstract double get(String name, double defaultValue)
311                throws IOException, IllegalArgumentException;
312
313        /**
314         * Gets the value of the object field identified by {@code name} from
315         * the persistent field.
316         *
317         * @param name
318         *            the name of the field to get.
319         * @param defaultValue
320         *            the default value that is used if the field does not have
321         *            a value when read from the source stream.
322         * @return the value of the field identified by {@code name}.
323         * @throws IOException
324         *             if an error occurs while reading from the source input
325         *             stream.
326         * @throws IllegalArgumentException
327         *             if the type of the field identified by {@code name} is
328         *             not {@code Object}.
329         */
330        public abstract Object get(String name, Object defaultValue)
331                throws IOException, IllegalArgumentException;
332    }
333
334    /**
335     * Constructs a new ObjectInputStream. This default constructor can be used
336     * by subclasses that do not want to use the public constructor if it
337     * allocates unneeded data.
338     *
339     * @throws IOException
340     *             if an error occurs when creating this stream.
341     */
342    protected ObjectInputStream() throws IOException {
343        super();
344        // WARNING - we should throw IOException if not called from a subclass
345        // according to the JavaDoc. Add the test.
346        this.subclassOverridingImplementation = true;
347    }
348
349    /**
350     * Constructs a new ObjectInputStream that reads from the InputStream
351     * {@code input}.
352     *
353     * @param input
354     *            the non-null source InputStream to filter reads on.
355     * @throws IOException
356     *             if an error occurs while reading the stream header.
357     * @throws StreamCorruptedException
358     *             if the source stream does not contain serialized objects that
359     *             can be read.
360     */
361    public ObjectInputStream(InputStream input) throws StreamCorruptedException, IOException {
362        this.input = (input instanceof DataInputStream)
363                ? (DataInputStream) input : new DataInputStream(input);
364        primitiveTypes = new DataInputStream(this);
365        enableResolve = false;
366        this.subclassOverridingImplementation = false;
367        resetState();
368        nestedLevels = 0;
369        // So read...() methods can be used by
370        // subclasses during readStreamHeader()
371        primitiveData = this.input;
372        // Has to be done here according to the specification
373        readStreamHeader();
374        primitiveData = emptyStream;
375    }
376
377    @Override
378    public int available() throws IOException {
379        // returns 0 if next data is an object, or N if reading primitive types
380        checkReadPrimitiveTypes();
381        return primitiveData.available();
382    }
383
384    /**
385     * Checks to if it is ok to read primitive types from this stream at
386     * this point. One is not supposed to read primitive types when about to
387     * read an object, for example, so an exception has to be thrown.
388     *
389     * @throws IOException
390     *             If any IO problem occurred when trying to read primitive type
391     *             or if it is illegal to read primitive types
392     */
393    private void checkReadPrimitiveTypes() throws IOException {
394        // If we still have primitive data, it is ok to read primitive data
395        if (primitiveData == input || primitiveData.available() > 0) {
396            return;
397        }
398
399        // If we got here either we had no Stream previously created or
400        // we no longer have data in that one, so get more bytes
401        do {
402            int next = 0;
403            if (hasPushbackTC) {
404                hasPushbackTC = false;
405            } else {
406                next = input.read();
407                pushbackTC = (byte) next;
408            }
409            switch (pushbackTC) {
410                case TC_BLOCKDATA:
411                    primitiveData = new ByteArrayInputStream(readBlockData());
412                    return;
413                case TC_BLOCKDATALONG:
414                    primitiveData = new ByteArrayInputStream(readBlockDataLong());
415                    return;
416                case TC_RESET:
417                    resetState();
418                    break;
419                default:
420                    if (next != -1) {
421                        pushbackTC();
422                    }
423                    return;
424            }
425            // Only TC_RESET falls through
426        } while (true);
427    }
428
429    /**
430     * Closes this stream. This implementation closes the source stream.
431     *
432     * @throws IOException
433     *             if an error occurs while closing this stream.
434     */
435    @Override
436    public void close() throws IOException {
437        input.close();
438    }
439
440    /**
441     * Default method to read objects from this stream. Serializable fields
442     * defined in the object's class and superclasses are read from the source
443     * stream.
444     *
445     * @throws ClassNotFoundException
446     *             if the object's class cannot be found.
447     * @throws IOException
448     *             if an I/O error occurs while reading the object data.
449     * @throws NotActiveException
450     *             if this method is not called from {@code readObject()}.
451     * @see ObjectOutputStream#defaultWriteObject
452     */
453    public void defaultReadObject() throws IOException, ClassNotFoundException,
454            NotActiveException {
455        if (currentObject != null || !mustResolve) {
456            readFieldValues(currentObject, currentClass);
457        } else {
458            throw new NotActiveException();
459        }
460    }
461
462    /**
463     * Enables object replacement for this stream. By default this is not
464     * enabled. Only trusted subclasses (loaded with system class loader) are
465     * allowed to change this status.
466     *
467     * @param enable
468     *            {@code true} to enable object replacement; {@code false} to
469     *            disable it.
470     * @return the previous setting.
471     * @see #resolveObject
472     * @see ObjectOutputStream#enableReplaceObject
473     */
474    protected boolean enableResolveObject(boolean enable) {
475        boolean originalValue = enableResolve;
476        enableResolve = enable;
477        return originalValue;
478    }
479
480    /**
481     * Return the next {@code int} handle to be used to indicate cyclic
482     * references being loaded from the stream.
483     *
484     * @return the next handle to represent the next cyclic reference
485     */
486    private int nextHandle() {
487        return nextHandle++;
488    }
489
490    /**
491     * Return the next token code (TC) from the receiver, which indicates what
492     * kind of object follows
493     *
494     * @return the next TC from the receiver
495     *
496     * @throws IOException
497     *             If an IO error occurs
498     *
499     * @see ObjectStreamConstants
500     */
501    private byte nextTC() throws IOException {
502        if (hasPushbackTC) {
503            hasPushbackTC = false; // We are consuming it
504        } else {
505            // Just in case a later call decides to really push it back,
506            // we don't require the caller to pass it as parameter
507            pushbackTC = input.readByte();
508        }
509        return pushbackTC;
510    }
511
512    /**
513     * Pushes back the last TC code read
514     */
515    private void pushbackTC() {
516        hasPushbackTC = true;
517    }
518
519    /**
520     * Reads a single byte from the source stream and returns it as an integer
521     * in the range from 0 to 255. Returns -1 if the end of the source stream
522     * has been reached. Blocks if no input is available.
523     *
524     * @return the byte read or -1 if the end of the source stream has been
525     *         reached.
526     * @throws IOException
527     *             if an error occurs while reading from this stream.
528     */
529    @Override
530    public int read() throws IOException {
531        checkReadPrimitiveTypes();
532        return primitiveData.read();
533    }
534
535    /**
536     * Reads at most {@code length} bytes from the source stream and stores them
537     * in byte array {@code buffer} starting at offset {@code count}. Blocks
538     * until {@code count} bytes have been read, the end of the source stream is
539     * detected or an exception is thrown.
540     *
541     * @param buffer
542     *            the array in which to store the bytes read.
543     * @param offset
544     *            the initial position in {@code buffer} to store the bytes
545     *            read from the source stream.
546     * @param length
547     *            the maximum number of bytes to store in {@code buffer}.
548     * @return the number of bytes read or -1 if the end of the source input
549     *         stream has been reached.
550     * @throws IndexOutOfBoundsException
551     *             if {@code offset < 0} or {@code length < 0}, or if
552     *             {@code offset + length} is greater than the length of
553     *             {@code buffer}.
554     * @throws IOException
555     *             if an error occurs while reading from this stream.
556     * @throws NullPointerException
557     *             if {@code buffer} is {@code null}.
558     */
559    @Override
560    public int read(byte[] buffer, int offset, int length) throws IOException {
561        Arrays.checkOffsetAndCount(buffer.length, offset, length);
562        if (length == 0) {
563            return 0;
564        }
565        checkReadPrimitiveTypes();
566        return primitiveData.read(buffer, offset, length);
567    }
568
569    /**
570     * Reads and returns an array of raw bytes with primitive data. The array
571     * will have up to 255 bytes. The primitive data will be in the format
572     * described by {@code DataOutputStream}.
573     *
574     * @return The primitive data read, as raw bytes
575     *
576     * @throws IOException
577     *             If an IO exception happened when reading the primitive data.
578     */
579    private byte[] readBlockData() throws IOException {
580        byte[] result = new byte[input.readByte() & 0xff];
581        input.readFully(result);
582        return result;
583    }
584
585    /**
586     * Reads and returns an array of raw bytes with primitive data. The array
587     * will have more than 255 bytes. The primitive data will be in the format
588     * described by {@code DataOutputStream}.
589     *
590     * @return The primitive data read, as raw bytes
591     *
592     * @throws IOException
593     *             If an IO exception happened when reading the primitive data.
594     */
595    private byte[] readBlockDataLong() throws IOException {
596        byte[] result = new byte[input.readInt()];
597        input.readFully(result);
598        return result;
599    }
600
601    /**
602     * Reads a boolean from the source stream.
603     *
604     * @return the boolean value read from the source stream.
605     * @throws EOFException
606     *             if the end of the input is reached before the read
607     *             request can be satisfied.
608     * @throws IOException
609     *             if an error occurs while reading from the source stream.
610     */
611    public boolean readBoolean() throws IOException {
612        return primitiveTypes.readBoolean();
613    }
614
615    /**
616     * Reads a byte (8 bit) from the source stream.
617     *
618     * @return the byte value read from the source stream.
619     * @throws EOFException
620     *             if the end of the input is reached before the read
621     *             request can be satisfied.
622     * @throws IOException
623     *             if an error occurs while reading from the source stream.
624     */
625    public byte readByte() throws IOException {
626        return primitiveTypes.readByte();
627    }
628
629    /**
630     * Reads a character (16 bit) from the source stream.
631     *
632     * @return the char value read from the source stream.
633     * @throws EOFException
634     *             if the end of the input is reached before the read
635     *             request can be satisfied.
636     * @throws IOException
637     *             if an error occurs while reading from the source stream.
638     */
639    public char readChar() throws IOException {
640        return primitiveTypes.readChar();
641    }
642
643    /**
644     * Reads and discards block data and objects until TC_ENDBLOCKDATA is found.
645     *
646     * @throws IOException
647     *             If an IO exception happened when reading the optional class
648     *             annotation.
649     * @throws ClassNotFoundException
650     *             If the class corresponding to the class descriptor could not
651     *             be found.
652     */
653    private void discardData() throws ClassNotFoundException, IOException {
654        primitiveData = emptyStream;
655        boolean resolve = mustResolve;
656        mustResolve = false;
657        do {
658            byte tc = nextTC();
659            if (tc == TC_ENDBLOCKDATA) {
660                mustResolve = resolve;
661                return; // End of annotation
662            }
663            readContent(tc);
664        } while (true);
665    }
666
667    /**
668     * Reads a class descriptor (an {@code ObjectStreamClass}) from the
669     * stream.
670     *
671     * @return the class descriptor read from the stream
672     *
673     * @throws IOException
674     *             If an IO exception happened when reading the class
675     *             descriptor.
676     * @throws ClassNotFoundException
677     *             If the class corresponding to the class descriptor could not
678     *             be found.
679     */
680    private ObjectStreamClass readClassDesc() throws ClassNotFoundException, IOException {
681        byte tc = nextTC();
682        switch (tc) {
683            case TC_CLASSDESC:
684                return readNewClassDesc(false);
685            case TC_PROXYCLASSDESC:
686                Class<?> proxyClass = readNewProxyClassDesc();
687                ObjectStreamClass streamClass = ObjectStreamClass.lookup(proxyClass);
688                streamClass.setLoadFields(ObjectStreamClass.NO_FIELDS);
689                registerObjectRead(streamClass, nextHandle(), false);
690                checkedSetSuperClassDesc(streamClass, readClassDesc());
691                return streamClass;
692            case TC_REFERENCE:
693                return (ObjectStreamClass) readCyclicReference();
694            case TC_NULL:
695                return null;
696            default:
697                throw corruptStream(tc);
698        }
699    }
700
701    private StreamCorruptedException corruptStream(byte tc) throws StreamCorruptedException {
702        throw new StreamCorruptedException("Wrong format: " + Integer.toHexString(tc & 0xff));
703    }
704
705    /**
706     * Reads the content of the receiver based on the previously read token
707     * {@code tc}.
708     *
709     * @param tc
710     *            The token code for the next item in the stream
711     * @return the object read from the stream
712     *
713     * @throws IOException
714     *             If an IO exception happened when reading the class
715     *             descriptor.
716     * @throws ClassNotFoundException
717     *             If the class corresponding to the object being read could not
718     *             be found.
719     */
720    private Object readContent(byte tc) throws ClassNotFoundException,
721            IOException {
722        switch (tc) {
723            case TC_BLOCKDATA:
724                return readBlockData();
725            case TC_BLOCKDATALONG:
726                return readBlockDataLong();
727            case TC_CLASS:
728                return readNewClass(false);
729            case TC_CLASSDESC:
730                return readNewClassDesc(false);
731            case TC_ARRAY:
732                return readNewArray(false);
733            case TC_OBJECT:
734                return readNewObject(false);
735            case TC_STRING:
736                return readNewString(false);
737            case TC_LONGSTRING:
738                return readNewLongString(false);
739            case TC_REFERENCE:
740                return readCyclicReference();
741            case TC_NULL:
742                return null;
743            case TC_EXCEPTION:
744                Exception exc = readException();
745                throw new WriteAbortedException("Read an exception", exc);
746            case TC_RESET:
747                resetState();
748                return null;
749            default:
750                throw corruptStream(tc);
751        }
752    }
753
754    /**
755     * Reads the content of the receiver based on the previously read token
756     * {@code tc}. Primitive data content is considered an error.
757     *
758     * @param unshared
759     *            read the object unshared
760     * @return the object read from the stream
761     *
762     * @throws IOException
763     *             If an IO exception happened when reading the class
764     *             descriptor.
765     * @throws ClassNotFoundException
766     *             If the class corresponding to the object being read could not
767     *             be found.
768     */
769    private Object readNonPrimitiveContent(boolean unshared)
770            throws ClassNotFoundException, IOException {
771        checkReadPrimitiveTypes();
772        if (primitiveData.available() > 0) {
773            OptionalDataException e = new OptionalDataException();
774            e.length = primitiveData.available();
775            throw e;
776        }
777
778        do {
779            byte tc = nextTC();
780            switch (tc) {
781                case TC_CLASS:
782                    return readNewClass(unshared);
783                case TC_CLASSDESC:
784                    return readNewClassDesc(unshared);
785                case TC_ARRAY:
786                    return readNewArray(unshared);
787                case TC_OBJECT:
788                    return readNewObject(unshared);
789                case TC_STRING:
790                    return readNewString(unshared);
791                case TC_LONGSTRING:
792                    return readNewLongString(unshared);
793                case TC_ENUM:
794                    return readEnum(unshared);
795                case TC_REFERENCE:
796                    if (unshared) {
797                        readNewHandle();
798                        throw new InvalidObjectException("Unshared read of back reference");
799                    }
800                    return readCyclicReference();
801                case TC_NULL:
802                    return null;
803                case TC_EXCEPTION:
804                    Exception exc = readException();
805                    throw new WriteAbortedException("Read an exception", exc);
806                case TC_RESET:
807                    resetState();
808                    break;
809                case TC_ENDBLOCKDATA: // Can occur reading class annotation
810                    pushbackTC();
811                    OptionalDataException e = new OptionalDataException();
812                    e.eof = true;
813                    throw e;
814                default:
815                    throw corruptStream(tc);
816            }
817            // Only TC_RESET falls through
818        } while (true);
819    }
820
821    /**
822     * Reads the next item from the stream assuming it is a cyclic reference to
823     * an object previously read. Return the actual object previously read.
824     *
825     * @return the object previously read from the stream
826     *
827     * @throws IOException
828     *             If an IO exception happened when reading the class
829     *             descriptor.
830     * @throws InvalidObjectException
831     *             If the cyclic reference is not valid.
832     */
833    private Object readCyclicReference() throws InvalidObjectException, IOException {
834        return registeredObjectRead(readNewHandle());
835    }
836
837    /**
838     * Reads a double (64 bit) from the source stream.
839     *
840     * @return the double value read from the source stream.
841     * @throws EOFException
842     *             if the end of the input is reached before the read
843     *             request can be satisfied.
844     * @throws IOException
845     *             if an error occurs while reading from the source stream.
846     */
847    public double readDouble() throws IOException {
848        return primitiveTypes.readDouble();
849    }
850
851    /**
852     * Read the next item assuming it is an exception. The exception is not a
853     * regular instance in the object graph, but the exception instance that
854     * happened (if any) when dumping the original object graph. The set of seen
855     * objects will be reset just before and just after loading this exception
856     * object.
857     * <p>
858     * When exceptions are found normally in the object graph, they are loaded
859     * as a regular object, and not by this method. In that case, the set of
860     * "known objects" is not reset.
861     *
862     * @return the exception read
863     *
864     * @throws IOException
865     *             If an IO exception happened when reading the exception
866     *             object.
867     * @throws ClassNotFoundException
868     *             If a class could not be found when reading the object graph
869     *             for the exception
870     * @throws OptionalDataException
871     *             If optional data could not be found when reading the
872     *             exception graph
873     * @throws WriteAbortedException
874     *             If another exception was caused when dumping this exception
875     */
876    private Exception readException() throws WriteAbortedException,
877            OptionalDataException, ClassNotFoundException, IOException {
878
879        resetSeenObjects();
880
881        // Now we read the Throwable object that was saved
882        // WARNING - the grammar says it is a Throwable, but the
883        // WriteAbortedException constructor takes an Exception. So, we read an
884        // Exception from the stream
885        Exception exc = (Exception) readObject();
886
887        // We reset the receiver's state (the grammar has "reset" in normal
888        // font)
889        resetSeenObjects();
890        return exc;
891    }
892
893    /**
894     * Reads a collection of field descriptors (name, type name, etc) for the
895     * class descriptor {@code cDesc} (an {@code ObjectStreamClass})
896     *
897     * @param cDesc
898     *            The class descriptor (an {@code ObjectStreamClass})
899     *            for which to write field information
900     *
901     * @throws IOException
902     *             If an IO exception happened when reading the field
903     *             descriptors.
904     * @throws ClassNotFoundException
905     *             If a class for one of the field types could not be found
906     *
907     * @see #readObject()
908     */
909    private void readFieldDescriptors(ObjectStreamClass cDesc)
910            throws ClassNotFoundException, IOException {
911        short numFields = input.readShort();
912        ObjectStreamField[] fields = new ObjectStreamField[numFields];
913
914        // We set it now, but each element will be inserted in the array further
915        // down
916        cDesc.setLoadFields(fields);
917
918        // Check ObjectOutputStream.writeFieldDescriptors
919        for (short i = 0; i < numFields; i++) {
920            char typecode = (char) input.readByte();
921            String fieldName = input.readUTF();
922            boolean isPrimType = ObjectStreamClass.isPrimitiveType(typecode);
923            String classSig;
924            if (isPrimType) {
925                classSig = String.valueOf(typecode);
926            } else {
927                // The spec says it is a UTF, but experience shows they dump
928                // this String using writeObject (unlike the field name, which
929                // is saved with writeUTF).
930                // And if resolveObject is enabled, the classSig may be modified
931                // so that the original class descriptor cannot be read
932                // properly, so it is disabled.
933                boolean old = enableResolve;
934                try {
935                    enableResolve = false;
936                    classSig = (String) readObject();
937                } finally {
938                    enableResolve = old;
939                }
940            }
941
942            classSig = formatClassSig(classSig);
943            ObjectStreamField f = new ObjectStreamField(classSig, fieldName);
944            fields[i] = f;
945        }
946    }
947
948    /*
949     * Format the class signature for ObjectStreamField, for example,
950     * "[L[Ljava.lang.String;;" is converted to "[Ljava.lang.String;"
951     */
952    private static String formatClassSig(String classSig) {
953        int start = 0;
954        int end = classSig.length();
955
956        if (end <= 0) {
957            return classSig;
958        }
959
960        while (classSig.startsWith("[L", start)
961                && classSig.charAt(end - 1) == ';') {
962            start += 2;
963            end--;
964        }
965
966        if (start > 0) {
967            start -= 2;
968            end++;
969            return classSig.substring(start, end);
970        }
971        return classSig;
972    }
973
974    /**
975     * Reads the persistent fields of the object that is currently being read
976     * from the source stream. The values read are stored in a GetField object
977     * that provides access to the persistent fields. This GetField object is
978     * then returned.
979     *
980     * @return the GetField object from which persistent fields can be accessed
981     *         by name.
982     * @throws ClassNotFoundException
983     *             if the class of an object being deserialized can not be
984     *             found.
985     * @throws IOException
986     *             if an error occurs while reading from this stream.
987     * @throws NotActiveException
988     *             if this stream is currently not reading an object.
989     */
990    public GetField readFields() throws IOException, ClassNotFoundException, NotActiveException {
991        if (currentObject == null) {
992            throw new NotActiveException();
993        }
994        EmulatedFieldsForLoading result = new EmulatedFieldsForLoading(currentClass);
995        readFieldValues(result);
996        return result;
997    }
998
999    /**
1000     * Reads a collection of field values for the emulated fields
1001     * {@code emulatedFields}
1002     *
1003     * @param emulatedFields
1004     *            an {@code EmulatedFieldsForLoading}, concrete subclass
1005     *            of {@code GetField}
1006     *
1007     * @throws IOException
1008     *             If an IO exception happened when reading the field values.
1009     * @throws InvalidClassException
1010     *             If an incompatible type is being assigned to an emulated
1011     *             field.
1012     * @throws OptionalDataException
1013     *             If optional data could not be found when reading the
1014     *             exception graph
1015     *
1016     * @see #readFields
1017     * @see #readObject()
1018     */
1019    private void readFieldValues(EmulatedFieldsForLoading emulatedFields)
1020            throws OptionalDataException, InvalidClassException, IOException {
1021        EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields().slots();
1022        for (ObjectSlot element : slots) {
1023            element.defaulted = false;
1024            Class<?> type = element.field.getType();
1025            if (type == int.class) {
1026                element.fieldValue = input.readInt();
1027            } else if (type == byte.class) {
1028                element.fieldValue = input.readByte();
1029            } else if (type == char.class) {
1030                element.fieldValue = input.readChar();
1031            } else if (type == short.class) {
1032                element.fieldValue = input.readShort();
1033            } else if (type == boolean.class) {
1034                element.fieldValue = input.readBoolean();
1035            } else if (type == long.class) {
1036                element.fieldValue = input.readLong();
1037            } else if (type == float.class) {
1038                element.fieldValue = input.readFloat();
1039            } else if (type == double.class) {
1040                element.fieldValue = input.readDouble();
1041            } else {
1042                // Either array or Object
1043                try {
1044                    element.fieldValue = readObject();
1045                } catch (ClassNotFoundException cnf) {
1046                    // WARNING- Not sure this is the right thing to do. Write
1047                    // test case.
1048                    throw new InvalidClassException(cnf.toString());
1049                }
1050            }
1051        }
1052    }
1053
1054    /**
1055     * Reads a collection of field values for the class descriptor
1056     * {@code classDesc} (an {@code ObjectStreamClass}). The
1057     * values will be used to set instance fields in object {@code obj}.
1058     * This is the default mechanism, when emulated fields (an
1059     * {@code GetField}) are not used. Actual values to load are stored
1060     * directly into the object {@code obj}.
1061     *
1062     * @param obj
1063     *            Instance in which the fields will be set.
1064     * @param classDesc
1065     *            A class descriptor (an {@code ObjectStreamClass})
1066     *            defining which fields should be loaded.
1067     *
1068     * @throws IOException
1069     *             If an IO exception happened when reading the field values.
1070     * @throws InvalidClassException
1071     *             If an incompatible type is being assigned to an emulated
1072     *             field.
1073     * @throws OptionalDataException
1074     *             If optional data could not be found when reading the
1075     *             exception graph
1076     * @throws ClassNotFoundException
1077     *             If a class of an object being de-serialized can not be found
1078     *
1079     * @see #readFields
1080     * @see #readObject()
1081     */
1082    private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException {
1083        // Now we must read all fields and assign them to the receiver
1084        ObjectStreamField[] fields = classDesc.getLoadFields();
1085        fields = (fields == null) ? ObjectStreamClass.NO_FIELDS : fields;
1086        Class<?> declaringClass = classDesc.forClass();
1087        if (declaringClass == null && mustResolve) {
1088            throw new ClassNotFoundException(classDesc.getName());
1089        }
1090
1091        for (ObjectStreamField fieldDesc : fields) {
1092            Field field = classDesc.getReflectionField(fieldDesc);
1093            // We may not have been able to find the field, but we still need to read the value
1094            // and do the other checking, so there's no null check on 'field' here.
1095            try {
1096                Class<?> type = fieldDesc.getTypeInternal();
1097                if (type == byte.class) {
1098                    byte b = input.readByte();
1099                    if (field != null) {
1100                        field.setByte(obj, b);
1101                    }
1102                } else if (type == char.class) {
1103                    char c = input.readChar();
1104                    if (field != null) {
1105                        field.setChar(obj, c);
1106                    }
1107                } else if (type == double.class) {
1108                    double d = input.readDouble();
1109                    if (field != null) {
1110                        field.setDouble(obj, d);
1111                    }
1112                } else if (type == float.class) {
1113                    float f = input.readFloat();
1114                    if (field != null) {
1115                        field.setFloat(obj, f);
1116                    }
1117                } else if (type == int.class) {
1118                    int i = input.readInt();
1119                    if (field != null) {
1120                        field.setInt(obj, i);
1121                    }
1122                } else if (type == long.class) {
1123                    long j = input.readLong();
1124                    if (field != null) {
1125                        field.setLong(obj, j);
1126                    }
1127                } else if (type == short.class) {
1128                    short s = input.readShort();
1129                    if (field != null) {
1130                        field.setShort(obj, s);
1131                    }
1132                } else if (type == boolean.class) {
1133                    boolean z = input.readBoolean();
1134                    if (field != null) {
1135                        field.setBoolean(obj, z);
1136                    }
1137                } else {
1138                    Object toSet = fieldDesc.isUnshared() ? readUnshared() : readObject();
1139                    if (toSet != null) {
1140                        // Get the field type from the local field rather than
1141                        // from the stream's supplied data. That's the field
1142                        // we'll be setting, so that's the one that needs to be
1143                        // validated.
1144                        String fieldName = fieldDesc.getName();
1145                        ObjectStreamField localFieldDesc = classDesc.getField(fieldName);
1146                        Class<?> fieldType = localFieldDesc.getTypeInternal();
1147                        Class<?> valueType = toSet.getClass();
1148                        if (!fieldType.isAssignableFrom(valueType)) {
1149                            throw new ClassCastException(classDesc.getName() + "." + fieldName + " - " + fieldType + " not compatible with " + valueType);
1150                        }
1151                        if (field != null) {
1152                            field.set(obj, toSet);
1153                        }
1154                    }
1155                }
1156            } catch (IllegalAccessException iae) {
1157                // ObjectStreamField should have called setAccessible(true).
1158                throw new AssertionError(iae);
1159            } catch (NoSuchFieldError ignored) {
1160            }
1161        }
1162    }
1163
1164    /**
1165     * Reads a float (32 bit) from the source stream.
1166     *
1167     * @return the float value read from the source stream.
1168     * @throws EOFException
1169     *             if the end of the input is reached before the read
1170     *             request can be satisfied.
1171     * @throws IOException
1172     *             if an error occurs while reading from the source stream.
1173     */
1174    public float readFloat() throws IOException {
1175        return primitiveTypes.readFloat();
1176    }
1177
1178    /**
1179     * Reads bytes from the source stream into the byte array {@code dst}.
1180     * This method will block until {@code dst.length} bytes have been read.
1181     *
1182     * @param dst
1183     *            the array in which to store the bytes read.
1184     * @throws EOFException
1185     *             if the end of the input is reached before the read
1186     *             request can be satisfied.
1187     * @throws IOException
1188     *             if an error occurs while reading from the source stream.
1189     */
1190    public void readFully(byte[] dst) throws IOException {
1191        primitiveTypes.readFully(dst);
1192    }
1193
1194    /**
1195     * Reads {@code byteCount} bytes from the source stream into the byte array {@code dst}.
1196     *
1197     * @param dst
1198     *            the byte array in which to store the bytes read.
1199     * @param offset
1200     *            the initial position in {@code dst} to store the bytes
1201     *            read from the source stream.
1202     * @param byteCount
1203     *            the number of bytes to read.
1204     * @throws EOFException
1205     *             if the end of the input is reached before the read
1206     *             request can be satisfied.
1207     * @throws IOException
1208     *             if an error occurs while reading from the source stream.
1209     */
1210    public void readFully(byte[] dst, int offset, int byteCount) throws IOException {
1211        primitiveTypes.readFully(dst, offset, byteCount);
1212    }
1213
1214    /**
1215     * Walks the hierarchy of classes described by class descriptor
1216     * {@code classDesc} and reads the field values corresponding to
1217     * fields declared by the corresponding class descriptor. The instance to
1218     * store field values into is {@code object}. If the class
1219     * (corresponding to class descriptor {@code classDesc}) defines
1220     * private instance method {@code readObject} it will be used to load
1221     * field values.
1222     *
1223     * @param object
1224     *            Instance into which stored field values loaded.
1225     * @param classDesc
1226     *            A class descriptor (an {@code ObjectStreamClass})
1227     *            defining which fields should be loaded.
1228     *
1229     * @throws IOException
1230     *             If an IO exception happened when reading the field values in
1231     *             the hierarchy.
1232     * @throws ClassNotFoundException
1233     *             If a class for one of the field types could not be found
1234     * @throws NotActiveException
1235     *             If {@code defaultReadObject} is called from the wrong
1236     *             context.
1237     *
1238     * @see #defaultReadObject
1239     * @see #readObject()
1240     */
1241    private void readHierarchy(Object object, ObjectStreamClass classDesc)
1242            throws IOException, ClassNotFoundException, NotActiveException {
1243        if (object == null && mustResolve) {
1244            throw new NotActiveException();
1245        }
1246
1247        List<ObjectStreamClass> streamClassList = classDesc.getHierarchy();
1248        if (object == null) {
1249            for (ObjectStreamClass objectStreamClass : streamClassList) {
1250                readObjectForClass(null, objectStreamClass);
1251            }
1252        } else {
1253            List<Class<?>> superclasses = cachedSuperclasses.get(object.getClass());
1254            if (superclasses == null) {
1255                superclasses = cacheSuperclassesFor(object.getClass());
1256            }
1257
1258            int lastIndex = 0;
1259            for (int i = 0, end = superclasses.size(); i < end; ++i) {
1260                Class<?> superclass = superclasses.get(i);
1261                int index = findStreamSuperclass(superclass, streamClassList, lastIndex);
1262                if (index == -1) {
1263                    readObjectNoData(object, superclass,
1264                            ObjectStreamClass.lookupStreamClass(superclass));
1265                } else {
1266                    for (int j = lastIndex; j <= index; j++) {
1267                        readObjectForClass(object, streamClassList.get(j));
1268                    }
1269                    lastIndex = index + 1;
1270                }
1271            }
1272        }
1273    }
1274
1275    private HashMap<Class<?>, List<Class<?>>> cachedSuperclasses = new HashMap<Class<?>, List<Class<?>>>();
1276
1277    private List<Class<?>> cacheSuperclassesFor(Class<?> c) {
1278        ArrayList<Class<?>> result = new ArrayList<Class<?>>();
1279        Class<?> nextClass = c;
1280        while (nextClass != null) {
1281            Class<?> testClass = nextClass.getSuperclass();
1282            if (testClass != null) {
1283                result.add(0, nextClass);
1284            }
1285            nextClass = testClass;
1286        }
1287        cachedSuperclasses.put(c, result);
1288        return result;
1289    }
1290
1291    private int findStreamSuperclass(Class<?> cl, List<ObjectStreamClass> classList, int lastIndex) {
1292        for (int i = lastIndex, end = classList.size(); i < end; i++) {
1293            ObjectStreamClass objCl = classList.get(i);
1294            String forName = objCl.forClass().getName();
1295
1296            if (objCl.getName().equals(forName)) {
1297                if (cl.getName().equals(objCl.getName())) {
1298                    return i;
1299                }
1300            } else {
1301                // there was a class replacement
1302                if (cl.getName().equals(forName)) {
1303                    return i;
1304                }
1305            }
1306        }
1307        return -1;
1308    }
1309
1310    private void readObjectNoData(Object object, Class<?> cl, ObjectStreamClass classDesc)
1311            throws ObjectStreamException {
1312        if (!classDesc.isSerializable()) {
1313            return;
1314        }
1315        if (classDesc.hasMethodReadObjectNoData()){
1316            final Method readMethod = classDesc.getMethodReadObjectNoData();
1317            try {
1318                readMethod.invoke(object);
1319            } catch (InvocationTargetException e) {
1320                Throwable ex = e.getTargetException();
1321                if (ex instanceof RuntimeException) {
1322                    throw (RuntimeException) ex;
1323                } else if (ex instanceof Error) {
1324                    throw (Error) ex;
1325                }
1326                throw (ObjectStreamException) ex;
1327            } catch (IllegalAccessException e) {
1328                throw new RuntimeException(e.toString());
1329            }
1330        }
1331
1332    }
1333
1334    private void readObjectForClass(Object object, ObjectStreamClass classDesc)
1335            throws IOException, ClassNotFoundException, NotActiveException {
1336        // Have to do this before calling defaultReadObject or anything that
1337        // calls defaultReadObject
1338        currentObject = object;
1339        currentClass = classDesc;
1340
1341        boolean hadWriteMethod = (classDesc.getFlags() & SC_WRITE_METHOD) != 0;
1342        Class<?> targetClass = classDesc.forClass();
1343
1344        final Method readMethod;
1345        if (targetClass == null || !mustResolve) {
1346            readMethod = null;
1347        } else {
1348            readMethod = classDesc.getMethodReadObject();
1349        }
1350        try {
1351            if (readMethod != null) {
1352                // We have to be able to fetch its value, even if it is private
1353                readMethod.setAccessible(true);
1354                try {
1355                    readMethod.invoke(object, this);
1356                } catch (InvocationTargetException e) {
1357                    Throwable ex = e.getTargetException();
1358                    if (ex instanceof ClassNotFoundException) {
1359                        throw (ClassNotFoundException) ex;
1360                    } else if (ex instanceof RuntimeException) {
1361                        throw (RuntimeException) ex;
1362                    } else if (ex instanceof Error) {
1363                        throw (Error) ex;
1364                    }
1365                    throw (IOException) ex;
1366                } catch (IllegalAccessException e) {
1367                    throw new RuntimeException(e.toString());
1368                }
1369            } else {
1370                defaultReadObject();
1371            }
1372            if (hadWriteMethod) {
1373                discardData();
1374            }
1375        } finally {
1376            // Cleanup, needs to run always so that we can later detect invalid
1377            // calls to defaultReadObject
1378            currentObject = null; // We did not set this, so we do not need to
1379            // clean it
1380            currentClass = null;
1381        }
1382    }
1383
1384    /**
1385     * Reads an integer (32 bit) from the source stream.
1386     *
1387     * @return the integer value read from the source stream.
1388     * @throws EOFException
1389     *             if the end of the input is reached before the read
1390     *             request can be satisfied.
1391     * @throws IOException
1392     *             if an error occurs while reading from the source stream.
1393     */
1394    public int readInt() throws IOException {
1395        return primitiveTypes.readInt();
1396    }
1397
1398    /**
1399     * Reads the next line from the source stream. Lines are terminated by
1400     * {@code '\r'}, {@code '\n'}, {@code "\r\n"} or an {@code EOF}.
1401     *
1402     * @return the string read from the source stream.
1403     * @throws IOException
1404     *             if an error occurs while reading from the source stream.
1405     * @deprecated Use {@link BufferedReader}
1406     */
1407    @Deprecated
1408    public String readLine() throws IOException {
1409        return primitiveTypes.readLine();
1410    }
1411
1412    /**
1413     * Reads a long (64 bit) from the source stream.
1414     *
1415     * @return the long value read from the source stream.
1416     * @throws EOFException
1417     *             if the end of the input is reached before the read
1418     *             request can be satisfied.
1419     * @throws IOException
1420     *             if an error occurs while reading from the source stream.
1421     */
1422    public long readLong() throws IOException {
1423        return primitiveTypes.readLong();
1424    }
1425
1426    /**
1427     * Read a new array from the receiver. It is assumed the array has not been
1428     * read yet (not a cyclic reference). Return the array read.
1429     *
1430     * @param unshared
1431     *            read the object unshared
1432     * @return the array read
1433     *
1434     * @throws IOException
1435     *             If an IO exception happened when reading the array.
1436     * @throws ClassNotFoundException
1437     *             If a class for one of the objects could not be found
1438     * @throws OptionalDataException
1439     *             If optional data could not be found when reading the array.
1440     */
1441    private Object readNewArray(boolean unshared) throws OptionalDataException,
1442            ClassNotFoundException, IOException {
1443        ObjectStreamClass classDesc = readClassDesc();
1444
1445        if (classDesc == null) {
1446            throw missingClassDescriptor();
1447        }
1448
1449        int newHandle = nextHandle();
1450
1451        // Array size
1452        int size = input.readInt();
1453        Class<?> arrayClass = classDesc.forClass();
1454        Class<?> componentType = arrayClass.getComponentType();
1455        Object result = Array.newInstance(componentType, size);
1456
1457        registerObjectRead(result, newHandle, unshared);
1458
1459        // Now we have code duplication just because Java is typed. We have to
1460        // read N elements and assign to array positions, but we must typecast
1461        // the array first, and also call different methods depending on the
1462        // elements.
1463        if (componentType.isPrimitive()) {
1464            if (componentType == int.class) {
1465                int[] intArray = (int[]) result;
1466                for (int i = 0; i < size; i++) {
1467                    intArray[i] = input.readInt();
1468                }
1469            } else if (componentType == byte.class) {
1470                byte[] byteArray = (byte[]) result;
1471                input.readFully(byteArray, 0, size);
1472            } else if (componentType == char.class) {
1473                char[] charArray = (char[]) result;
1474                for (int i = 0; i < size; i++) {
1475                    charArray[i] = input.readChar();
1476                }
1477            } else if (componentType == short.class) {
1478                short[] shortArray = (short[]) result;
1479                for (int i = 0; i < size; i++) {
1480                    shortArray[i] = input.readShort();
1481                }
1482            } else if (componentType == boolean.class) {
1483                boolean[] booleanArray = (boolean[]) result;
1484                for (int i = 0; i < size; i++) {
1485                    booleanArray[i] = input.readBoolean();
1486                }
1487            } else if (componentType == long.class) {
1488                long[] longArray = (long[]) result;
1489                for (int i = 0; i < size; i++) {
1490                    longArray[i] = input.readLong();
1491                }
1492            } else if (componentType == float.class) {
1493                float[] floatArray = (float[]) result;
1494                for (int i = 0; i < size; i++) {
1495                    floatArray[i] = input.readFloat();
1496                }
1497            } else if (componentType == double.class) {
1498                double[] doubleArray = (double[]) result;
1499                for (int i = 0; i < size; i++) {
1500                    doubleArray[i] = input.readDouble();
1501                }
1502            } else {
1503                throw new ClassNotFoundException("Wrong base type in " + classDesc.getName());
1504            }
1505        } else {
1506            // Array of Objects
1507            Object[] objectArray = (Object[]) result;
1508            for (int i = 0; i < size; i++) {
1509                // TODO: This place is the opportunity for enhancement
1510                //      We can implement writing elements through fast-path,
1511                //      without setting up the context (see readObject()) for
1512                //      each element with public API
1513                objectArray[i] = readObject();
1514            }
1515        }
1516        if (enableResolve) {
1517            result = resolveObject(result);
1518            registerObjectRead(result, newHandle, false);
1519        }
1520        return result;
1521    }
1522
1523    /**
1524     * Reads a new class from the receiver. It is assumed the class has not been
1525     * read yet (not a cyclic reference). Return the class read.
1526     *
1527     * @param unshared
1528     *            read the object unshared
1529     * @return The {@code java.lang.Class} read from the stream.
1530     *
1531     * @throws IOException
1532     *             If an IO exception happened when reading the class.
1533     * @throws ClassNotFoundException
1534     *             If a class for one of the objects could not be found
1535     */
1536    private Class<?> readNewClass(boolean unshared) throws ClassNotFoundException, IOException {
1537        ObjectStreamClass classDesc = readClassDesc();
1538        if (classDesc == null) {
1539            throw missingClassDescriptor();
1540        }
1541        Class<?> localClass = classDesc.forClass();
1542        if (localClass != null) {
1543            registerObjectRead(localClass, nextHandle(), unshared);
1544        }
1545        return localClass;
1546    }
1547
1548    /*
1549     * read class type for Enum, note there's difference between enum and normal
1550     * classes
1551     */
1552    private ObjectStreamClass readEnumDesc() throws IOException,
1553            ClassNotFoundException {
1554        byte tc = nextTC();
1555        switch (tc) {
1556            case TC_CLASSDESC:
1557                return readEnumDescInternal();
1558            case TC_REFERENCE:
1559                return (ObjectStreamClass) readCyclicReference();
1560            case TC_NULL:
1561                return null;
1562            default:
1563                throw corruptStream(tc);
1564        }
1565    }
1566
1567    private ObjectStreamClass readEnumDescInternal() throws IOException, ClassNotFoundException {
1568        ObjectStreamClass classDesc;
1569        primitiveData = input;
1570        int oldHandle = descriptorHandle;
1571        descriptorHandle = nextHandle();
1572        classDesc = readClassDescriptor();
1573        registerObjectRead(classDesc, descriptorHandle, false);
1574        descriptorHandle = oldHandle;
1575        primitiveData = emptyStream;
1576        classDesc.setClass(resolveClass(classDesc));
1577        // Consume unread class annotation data and TC_ENDBLOCKDATA
1578        discardData();
1579        ObjectStreamClass superClass = readClassDesc();
1580        checkedSetSuperClassDesc(classDesc, superClass);
1581        // Check SUIDs, note all SUID for Enum is 0L
1582        if (0L != classDesc.getSerialVersionUID() || 0L != superClass.getSerialVersionUID()) {
1583            throw new InvalidClassException(superClass.getName(),
1584                    "Incompatible class (SUID): " + superClass + " but expected " + superClass);
1585        }
1586        byte tc = nextTC();
1587        // discard TC_ENDBLOCKDATA after classDesc if any
1588        if (tc == TC_ENDBLOCKDATA) {
1589            // read next parent class. For enum, it may be null
1590            superClass.setSuperclass(readClassDesc());
1591        } else {
1592            // not TC_ENDBLOCKDATA, push back for next read
1593            pushbackTC();
1594        }
1595        return classDesc;
1596    }
1597
1598    @SuppressWarnings("unchecked")// For the Enum.valueOf call
1599    private Object readEnum(boolean unshared) throws OptionalDataException,
1600            ClassNotFoundException, IOException {
1601        // read classdesc for Enum first
1602        ObjectStreamClass classDesc = readEnumDesc();
1603        int newHandle = nextHandle();
1604        // read name after class desc
1605        String name;
1606        byte tc = nextTC();
1607        switch (tc) {
1608            case TC_REFERENCE:
1609                if (unshared) {
1610                    readNewHandle();
1611                    throw new InvalidObjectException("Unshared read of back reference");
1612                }
1613                name = (String) readCyclicReference();
1614                break;
1615            case TC_STRING:
1616                name = (String) readNewString(unshared);
1617                break;
1618            default:
1619                throw corruptStream(tc);
1620        }
1621
1622        Enum<?> result = Enum.valueOf((Class) classDesc.forClass(), name);
1623        registerObjectRead(result, newHandle, unshared);
1624
1625        return result;
1626    }
1627
1628    /**
1629     * Reads a new class descriptor from the receiver. It is assumed the class
1630     * descriptor has not been read yet (not a cyclic reference). Return the
1631     * class descriptor read.
1632     *
1633     * @param unshared
1634     *            read the object unshared
1635     * @return The {@code ObjectStreamClass} read from the stream.
1636     *
1637     * @throws IOException
1638     *             If an IO exception happened when reading the class
1639     *             descriptor.
1640     * @throws ClassNotFoundException
1641     *             If a class for one of the objects could not be found
1642     */
1643    private ObjectStreamClass readNewClassDesc(boolean unshared)
1644            throws ClassNotFoundException, IOException {
1645        // So read...() methods can be used by
1646        // subclasses during readClassDescriptor()
1647        primitiveData = input;
1648        int oldHandle = descriptorHandle;
1649        descriptorHandle = nextHandle();
1650        ObjectStreamClass newClassDesc = readClassDescriptor();
1651        registerObjectRead(newClassDesc, descriptorHandle, unshared);
1652        descriptorHandle = oldHandle;
1653        primitiveData = emptyStream;
1654
1655        // We need to map classDesc to class.
1656        try {
1657            newClassDesc.setClass(resolveClass(newClassDesc));
1658            // Check SUIDs & base name of the class
1659            verifyAndInit(newClassDesc);
1660        } catch (ClassNotFoundException e) {
1661            if (mustResolve) {
1662                throw e;
1663                // Just continue, the class may not be required
1664            }
1665        }
1666
1667        // Resolve the field signatures using the class loader of the
1668        // resolved class
1669        ObjectStreamField[] fields = newClassDesc.getLoadFields();
1670        fields = (fields == null) ? ObjectStreamClass.NO_FIELDS : fields;
1671        ClassLoader loader = newClassDesc.forClass() == null ? callerClassLoader
1672                : newClassDesc.forClass().getClassLoader();
1673        for (ObjectStreamField element : fields) {
1674            element.resolve(loader);
1675        }
1676
1677        // Consume unread class annotation data and TC_ENDBLOCKDATA
1678        discardData();
1679        checkedSetSuperClassDesc(newClassDesc, readClassDesc());
1680        return newClassDesc;
1681    }
1682
1683    /**
1684     * Reads a new proxy class descriptor from the receiver. It is assumed the
1685     * proxy class descriptor has not been read yet (not a cyclic reference).
1686     * Return the proxy class descriptor read.
1687     *
1688     * @return The {@code Class} read from the stream.
1689     *
1690     * @throws IOException
1691     *             If an IO exception happened when reading the class
1692     *             descriptor.
1693     * @throws ClassNotFoundException
1694     *             If a class for one of the objects could not be found
1695     */
1696    private Class<?> readNewProxyClassDesc() throws ClassNotFoundException,
1697            IOException {
1698        int count = input.readInt();
1699        String[] interfaceNames = new String[count];
1700        for (int i = 0; i < count; i++) {
1701            interfaceNames[i] = input.readUTF();
1702        }
1703        Class<?> proxy = resolveProxyClass(interfaceNames);
1704        // Consume unread class annotation data and TC_ENDBLOCKDATA
1705        discardData();
1706        return proxy;
1707    }
1708
1709    /**
1710     * Reads a class descriptor from the source stream.
1711     *
1712     * @return the class descriptor read from the source stream.
1713     * @throws ClassNotFoundException
1714     *             if a class for one of the objects cannot be found.
1715     * @throws IOException
1716     *             if an error occurs while reading from the source stream.
1717     */
1718    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
1719        ObjectStreamClass newClassDesc = new ObjectStreamClass();
1720        String name = input.readUTF();
1721        if (name.length() == 0) {
1722            throw new IOException("The stream is corrupted");
1723        }
1724        newClassDesc.setName(name);
1725        newClassDesc.setSerialVersionUID(input.readLong());
1726        newClassDesc.setFlags(input.readByte());
1727
1728        /*
1729         * We must register the class descriptor before reading field
1730         * descriptors. If called outside of readObject, the descriptorHandle
1731         * might be unset.
1732         */
1733        if (descriptorHandle == -1) {
1734            descriptorHandle = nextHandle();
1735        }
1736        registerObjectRead(newClassDesc, descriptorHandle, false);
1737
1738        readFieldDescriptors(newClassDesc);
1739        return newClassDesc;
1740    }
1741
1742    /**
1743     * Creates the proxy class that implements the interfaces specified in
1744     * {@code interfaceNames}.
1745     *
1746     * @param interfaceNames
1747     *            the interfaces used to create the proxy class.
1748     * @return the proxy class.
1749     * @throws ClassNotFoundException
1750     *             if the proxy class or any of the specified interfaces cannot
1751     *             be created.
1752     * @throws IOException
1753     *             if an error occurs while reading from the source stream.
1754     * @see ObjectOutputStream#annotateProxyClass(Class)
1755     */
1756    protected Class<?> resolveProxyClass(String[] interfaceNames)
1757            throws IOException, ClassNotFoundException {
1758        // TODO: This method is opportunity for performance enhancement
1759        //       We can cache the classloader and recently used interfaces.
1760        ClassLoader loader = ClassLoader.getSystemClassLoader();
1761        Class<?>[] interfaces = new Class<?>[interfaceNames.length];
1762        for (int i = 0; i < interfaceNames.length; i++) {
1763            interfaces[i] = Class.forName(interfaceNames[i], false, loader);
1764        }
1765        try {
1766            return Proxy.getProxyClass(loader, interfaces);
1767        } catch (IllegalArgumentException e) {
1768            throw new ClassNotFoundException(e.toString(), e);
1769        }
1770    }
1771
1772    private int readNewHandle() throws IOException {
1773        return input.readInt();
1774    }
1775
1776    /**
1777     * Read a new object from the stream. It is assumed the object has not been
1778     * loaded yet (not a cyclic reference). Return the object read.
1779     *
1780     * If the object implements <code>Externalizable</code> its
1781     * <code>readExternal</code> is called. Otherwise, all fields described by
1782     * the class hierarchy are loaded. Each class can define how its declared
1783     * instance fields are loaded by defining a private method
1784     * <code>readObject</code>
1785     *
1786     * @param unshared
1787     *            read the object unshared
1788     * @return the object read
1789     *
1790     * @throws IOException
1791     *             If an IO exception happened when reading the object.
1792     * @throws OptionalDataException
1793     *             If optional data could not be found when reading the object
1794     *             graph
1795     * @throws ClassNotFoundException
1796     *             If a class for one of the objects could not be found
1797     */
1798    private Object readNewObject(boolean unshared)
1799            throws OptionalDataException, ClassNotFoundException, IOException {
1800        ObjectStreamClass classDesc = readClassDesc();
1801
1802        if (classDesc == null) {
1803            throw missingClassDescriptor();
1804        }
1805
1806        int newHandle = nextHandle();
1807        Class<?> objectClass = classDesc.forClass();
1808        Object result = null;
1809        Object registeredResult = null;
1810        if (objectClass != null) {
1811            // Now we know which class to instantiate and which constructor to
1812            // run. We are allowed to run the constructor.
1813            result = classDesc.newInstance(objectClass);
1814            registerObjectRead(result, newHandle, unshared);
1815            registeredResult = result;
1816        } else {
1817            result = null;
1818        }
1819
1820        try {
1821            // This is how we know what to do in defaultReadObject. And it is
1822            // also used by defaultReadObject to check if it was called from an
1823            // invalid place. It also allows readExternal to call
1824            // defaultReadObject and have it work.
1825            currentObject = result;
1826            currentClass = classDesc;
1827
1828            // If Externalizable, just let the object read itself
1829            // Note that this value comes from the Stream, and in fact it could be
1830            // that the classes have been changed so that the info below now
1831            // conflicts with the newer class
1832            boolean wasExternalizable = (classDesc.getFlags() & SC_EXTERNALIZABLE) != 0;
1833            if (wasExternalizable) {
1834                boolean blockData = (classDesc.getFlags() & SC_BLOCK_DATA) != 0;
1835                if (!blockData) {
1836                    primitiveData = input;
1837                }
1838                if (mustResolve) {
1839                    Externalizable extern = (Externalizable) result;
1840                    extern.readExternal(this);
1841                }
1842                if (blockData) {
1843                    // Similar to readHierarchy. Anything not read by
1844                    // readExternal has to be consumed here
1845                    discardData();
1846                } else {
1847                    primitiveData = emptyStream;
1848                }
1849            } else {
1850                // If we got here, it is Serializable but not Externalizable.
1851                // Walk the hierarchy reading each class' slots
1852                readHierarchy(result, classDesc);
1853            }
1854        } finally {
1855            // Cleanup, needs to run always so that we can later detect invalid
1856            // calls to defaultReadObject
1857            currentObject = null;
1858            currentClass = null;
1859        }
1860
1861        if (objectClass != null) {
1862
1863            if (classDesc.hasMethodReadResolve()){
1864                Method methodReadResolve = classDesc.getMethodReadResolve();
1865                try {
1866                    result = methodReadResolve.invoke(result, (Object[]) null);
1867                } catch (IllegalAccessException ignored) {
1868                } catch (InvocationTargetException ite) {
1869                    Throwable target = ite.getTargetException();
1870                    if (target instanceof ObjectStreamException) {
1871                        throw (ObjectStreamException) target;
1872                    } else if (target instanceof Error) {
1873                        throw (Error) target;
1874                    } else {
1875                        throw (RuntimeException) target;
1876                    }
1877                }
1878
1879            }
1880        }
1881        // We get here either if class-based replacement was not needed or if it
1882        // was needed but produced the same object or if it could not be
1883        // computed.
1884
1885        // The object to return is the one we instantiated or a replacement for
1886        // it
1887        if (result != null && enableResolve) {
1888            result = resolveObject(result);
1889        }
1890        if (registeredResult != result) {
1891            registerObjectRead(result, newHandle, unshared);
1892        }
1893        return result;
1894    }
1895
1896    private InvalidClassException missingClassDescriptor() throws InvalidClassException {
1897        throw new InvalidClassException("Read null attempting to read class descriptor for object");
1898    }
1899
1900    /**
1901     * Read a string encoded in {@link DataInput modified UTF-8} from the
1902     * receiver. Return the string read.
1903     *
1904     * @param unshared
1905     *            read the object unshared
1906     * @return the string just read.
1907     * @throws IOException
1908     *             If an IO exception happened when reading the String.
1909     */
1910    private Object readNewString(boolean unshared) throws IOException {
1911        Object result = input.readUTF();
1912        if (enableResolve) {
1913            result = resolveObject(result);
1914        }
1915        registerObjectRead(result, nextHandle(), unshared);
1916
1917        return result;
1918    }
1919
1920    /**
1921     * Read a new String in UTF format from the receiver. Return the string
1922     * read.
1923     *
1924     * @param unshared
1925     *            read the object unshared
1926     * @return the string just read.
1927     *
1928     * @throws IOException
1929     *             If an IO exception happened when reading the String.
1930     */
1931    private Object readNewLongString(boolean unshared) throws IOException {
1932        long length = input.readLong();
1933        Object result = input.decodeUTF((int) length);
1934        if (enableResolve) {
1935            result = resolveObject(result);
1936        }
1937        registerObjectRead(result, nextHandle(), unshared);
1938
1939        return result;
1940    }
1941
1942    /**
1943     * Reads the next object from the source stream.
1944     *
1945     * @return the object read from the source stream.
1946     * @throws ClassNotFoundException
1947     *             if the class of one of the objects in the object graph cannot
1948     *             be found.
1949     * @throws IOException
1950     *             if an error occurs while reading from the source stream.
1951     * @throws OptionalDataException
1952     *             if primitive data types were found instead of an object.
1953     * @see ObjectOutputStream#writeObject(Object)
1954     */
1955    public final Object readObject() throws OptionalDataException,
1956            ClassNotFoundException, IOException {
1957        return readObject(false);
1958    }
1959
1960    /**
1961     * Reads the next unshared object from the source stream.
1962     *
1963     * @return the new object read.
1964     * @throws ClassNotFoundException
1965     *             if the class of one of the objects in the object graph cannot
1966     *             be found.
1967     * @throws IOException
1968     *             if an error occurs while reading from the source stream.
1969     * @see ObjectOutputStream#writeUnshared
1970     */
1971    public Object readUnshared() throws IOException, ClassNotFoundException {
1972        return readObject(true);
1973    }
1974
1975    private Object readObject(boolean unshared) throws OptionalDataException,
1976            ClassNotFoundException, IOException {
1977        boolean restoreInput = (primitiveData == input);
1978        if (restoreInput) {
1979            primitiveData = emptyStream;
1980        }
1981
1982        // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow
1983        // behavior overriding.
1984        if (subclassOverridingImplementation && !unshared) {
1985            return readObjectOverride();
1986        }
1987
1988        // If we still had primitive types to read, should we discard them
1989        // (reset the primitiveTypes stream) or leave as is, so that attempts to
1990        // read primitive types won't read 'past data' ???
1991        Object result;
1992        try {
1993            // We need this so we can tell when we are returning to the
1994            // original/outside caller
1995            if (++nestedLevels == 1) {
1996                // Remember the caller's class loader
1997                callerClassLoader = getClosestUserClassLoader();
1998            }
1999
2000            result = readNonPrimitiveContent(unshared);
2001            if (restoreInput) {
2002                primitiveData = input;
2003            }
2004        } finally {
2005            // We need this so we can tell when we are returning to the
2006            // original/outside caller
2007            if (--nestedLevels == 0) {
2008                // We are going to return to the original caller, perform
2009                // cleanups.
2010                // No more need to remember the caller's class loader
2011                callerClassLoader = null;
2012            }
2013        }
2014
2015        // Done reading this object. Is it time to return to the original
2016        // caller? If so we need to perform validations first.
2017        if (nestedLevels == 0 && validations != null) {
2018            // We are going to return to the original caller. If validation is
2019            // enabled we need to run them now and then cleanup the validation
2020            // collection
2021            try {
2022                for (InputValidationDesc element : validations) {
2023                    element.validator.validateObject();
2024                }
2025            } finally {
2026                // Validations have to be renewed, since they are only called
2027                // from readObject
2028                validations = null;
2029            }
2030        }
2031        return result;
2032    }
2033
2034    private static final ClassLoader bootstrapLoader = Object.class.getClassLoader();
2035    private static final ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
2036
2037    /**
2038     * Searches up the call stack to find the closest user-defined class loader.
2039     *
2040     * @return a user-defined class loader or null if one isn't found
2041     */
2042    private static ClassLoader getClosestUserClassLoader() {
2043        Class<?>[] stackClasses = VMStack.getClasses(-1);
2044        for (Class<?> stackClass : stackClasses) {
2045            ClassLoader loader = stackClass.getClassLoader();
2046            if (loader != null && loader != bootstrapLoader
2047                    && loader != systemLoader) {
2048                return loader;
2049            }
2050        }
2051        return null;
2052    }
2053
2054    /**
2055     * Method to be overridden by subclasses to read the next object from the
2056     * source stream.
2057     *
2058     * @return the object read from the source stream.
2059     * @throws ClassNotFoundException
2060     *             if the class of one of the objects in the object graph cannot
2061     *             be found.
2062     * @throws IOException
2063     *             if an error occurs while reading from the source stream.
2064     * @throws OptionalDataException
2065     *             if primitive data types were found instead of an object.
2066     * @see ObjectOutputStream#writeObjectOverride
2067     */
2068    protected Object readObjectOverride() throws OptionalDataException,
2069            ClassNotFoundException, IOException {
2070        if (input == null) {
2071            return null;
2072        }
2073        // Subclasses must override.
2074        throw new IOException();
2075    }
2076
2077    /**
2078     * Reads a short (16 bit) from the source stream.
2079     *
2080     * @return the short value read from the source stream.
2081     * @throws IOException
2082     *             if an error occurs while reading from the source stream.
2083     */
2084    public short readShort() throws IOException {
2085        return primitiveTypes.readShort();
2086    }
2087
2088    /**
2089     * Reads and validates the ObjectInputStream header from the source stream.
2090     *
2091     * @throws IOException
2092     *             if an error occurs while reading from the source stream.
2093     * @throws StreamCorruptedException
2094     *             if the source stream does not contain readable serialized
2095     *             objects.
2096     */
2097    protected void readStreamHeader() throws IOException,
2098            StreamCorruptedException {
2099        if (input.readShort() == STREAM_MAGIC
2100                && input.readShort() == STREAM_VERSION) {
2101            return;
2102        }
2103        throw new StreamCorruptedException();
2104    }
2105
2106    /**
2107     * Reads an unsigned byte (8 bit) from the source stream.
2108     *
2109     * @return the unsigned byte value read from the source stream packaged in
2110     *         an integer.
2111     * @throws EOFException
2112     *             if the end of the input is reached before the read
2113     *             request can be satisfied.
2114     * @throws IOException
2115     *             if an error occurs while reading from the source stream.
2116     */
2117    public int readUnsignedByte() throws IOException {
2118        return primitiveTypes.readUnsignedByte();
2119    }
2120
2121    /**
2122     * Reads an unsigned short (16 bit) from the source stream.
2123     *
2124     * @return the unsigned short value read from the source stream packaged in
2125     *         an integer.
2126     * @throws EOFException
2127     *             if the end of the input is reached before the read
2128     *             request can be satisfied.
2129     * @throws IOException
2130     *             if an error occurs while reading from the source stream.
2131     */
2132    public int readUnsignedShort() throws IOException {
2133        return primitiveTypes.readUnsignedShort();
2134    }
2135
2136    /**
2137     * Reads a string encoded in {@link DataInput modified UTF-8} from the
2138     * source stream.
2139     *
2140     * @return the string encoded in {@link DataInput modified UTF-8} read from
2141     *         the source stream.
2142     * @throws EOFException
2143     *             if the end of the input is reached before the read
2144     *             request can be satisfied.
2145     * @throws IOException
2146     *             if an error occurs while reading from the source stream.
2147     */
2148    public String readUTF() throws IOException {
2149        return primitiveTypes.readUTF();
2150    }
2151
2152    /**
2153     * Returns the previously-read object corresponding to the given serialization handle.
2154     * @throws InvalidObjectException
2155     *             If there is no previously-read object with this handle
2156     */
2157    private Object registeredObjectRead(int handle) throws InvalidObjectException {
2158        Object res = objectsRead.get(handle - ObjectStreamConstants.baseWireHandle);
2159        if (res == UNSHARED_OBJ) {
2160            throw new InvalidObjectException("Cannot read back reference to unshared object");
2161        }
2162        return res;
2163    }
2164
2165    /**
2166     * Associates a read object with the its serialization handle.
2167     */
2168    private void registerObjectRead(Object obj, int handle, boolean unshared) throws IOException {
2169        if (unshared) {
2170            obj = UNSHARED_OBJ;
2171        }
2172        int index = handle - ObjectStreamConstants.baseWireHandle;
2173        int size = objectsRead.size();
2174        // ObjectOutputStream sometimes wastes a handle. I've compared hex dumps of the RI
2175        // and it seems like that's a 'feature'. Look for calls to objectsWritten.put that
2176        // are guarded by !unshared tests.
2177        while (index > size) {
2178            objectsRead.add(null);
2179            ++size;
2180        }
2181        if (index == size) {
2182            objectsRead.add(obj);
2183        } else {
2184            objectsRead.set(index, obj);
2185        }
2186    }
2187
2188    /**
2189     * Registers a callback for post-deserialization validation of objects. It
2190     * allows to perform additional consistency checks before the {@code
2191     * readObject()} method of this class returns its result to the caller. This
2192     * method can only be called from within the {@code readObject()} method of
2193     * a class that implements "special" deserialization rules. It can be called
2194     * multiple times. Validation callbacks are then done in order of decreasing
2195     * priority, defined by {@code priority}.
2196     *
2197     * @param object
2198     *            an object that can validate itself by receiving a callback.
2199     * @param priority
2200     *            the validator's priority.
2201     * @throws InvalidObjectException
2202     *             if {@code object} is {@code null}.
2203     * @throws NotActiveException
2204     *             if this stream is currently not reading objects. In that
2205     *             case, calling this method is not allowed.
2206     * @see ObjectInputValidation#validateObject()
2207     */
2208    public synchronized void registerValidation(ObjectInputValidation object,
2209            int priority) throws NotActiveException, InvalidObjectException {
2210        // Validation can only be registered when inside readObject calls
2211        Object instanceBeingRead = this.currentObject;
2212
2213        if (instanceBeingRead == null && nestedLevels == 0) {
2214            throw new NotActiveException();
2215        }
2216        if (object == null) {
2217            throw new InvalidObjectException("Callback object cannot be null");
2218        }
2219        // From now on it is just insertion in a SortedCollection. Since
2220        // the Java class libraries don't provide that, we have to
2221        // implement it from scratch here.
2222        InputValidationDesc desc = new InputValidationDesc();
2223        desc.validator = object;
2224        desc.priority = priority;
2225        // No need for this, validateObject does not take a parameter
2226        // desc.toValidate = instanceBeingRead;
2227        if (validations == null) {
2228            validations = new InputValidationDesc[1];
2229            validations[0] = desc;
2230        } else {
2231            int i = 0;
2232            for (; i < validations.length; i++) {
2233                InputValidationDesc validation = validations[i];
2234                // Sorted, higher priority first.
2235                if (priority >= validation.priority) {
2236                    break; // Found the index where to insert
2237                }
2238            }
2239            InputValidationDesc[] oldValidations = validations;
2240            int currentSize = oldValidations.length;
2241            validations = new InputValidationDesc[currentSize + 1];
2242            System.arraycopy(oldValidations, 0, validations, 0, i);
2243            System.arraycopy(oldValidations, i, validations, i + 1, currentSize
2244                    - i);
2245            validations[i] = desc;
2246        }
2247    }
2248
2249    /**
2250     * Reset the collection of objects already loaded by the receiver.
2251     */
2252    private void resetSeenObjects() {
2253        objectsRead = new ArrayList<Object>();
2254        nextHandle = baseWireHandle;
2255        primitiveData = emptyStream;
2256    }
2257
2258    /**
2259     * Reset the receiver. The collection of objects already read by the
2260     * receiver is reset, and internal structures are also reset so that the
2261     * receiver knows it is in a fresh clean state.
2262     */
2263    private void resetState() {
2264        resetSeenObjects();
2265        hasPushbackTC = false;
2266        pushbackTC = 0;
2267        // nestedLevels = 0;
2268    }
2269
2270    /**
2271     * Loads the Java class corresponding to the class descriptor {@code
2272     * osClass} that has just been read from the source stream.
2273     *
2274     * @param osClass
2275     *            an ObjectStreamClass read from the source stream.
2276     * @return a Class corresponding to the descriptor {@code osClass}.
2277     * @throws ClassNotFoundException
2278     *             if the class for an object cannot be found.
2279     * @throws IOException
2280     *             if an I/O error occurs while creating the class.
2281     * @see ObjectOutputStream#annotateClass(Class)
2282     */
2283    protected Class<?> resolveClass(ObjectStreamClass osClass)
2284            throws IOException, ClassNotFoundException {
2285        // fastpath: obtain cached value
2286        Class<?> cls = osClass.forClass();
2287        if (cls == null) {
2288            // slowpath: resolve the class
2289            String className = osClass.getName();
2290
2291            // if it is primitive class, for example, long.class
2292            cls = PRIMITIVE_CLASSES.get(className);
2293
2294            if (cls == null) {
2295                // not primitive class
2296                // Use the first non-null ClassLoader on the stack. If null, use
2297                // the system class loader
2298                cls = Class.forName(className, true, callerClassLoader);
2299            }
2300        }
2301        return cls;
2302    }
2303
2304    /**
2305     * Allows trusted subclasses to substitute the specified original {@code
2306     * object} with a new object. Object substitution has to be activated first
2307     * with calling {@code enableResolveObject(true)}. This implementation just
2308     * returns {@code object}.
2309     *
2310     * @param object
2311     *            the original object for which a replacement may be defined.
2312     * @return the replacement object for {@code object}.
2313     * @throws IOException
2314     *             if any I/O error occurs while creating the replacement
2315     *             object.
2316     * @see #enableResolveObject
2317     * @see ObjectOutputStream#enableReplaceObject
2318     * @see ObjectOutputStream#replaceObject
2319     */
2320    protected Object resolveObject(Object object) throws IOException {
2321        // By default no object replacement. Subclasses can override
2322        return object;
2323    }
2324
2325    /**
2326     * Skips {@code length} bytes on the source stream. This method should not
2327     * be used to skip bytes at any arbitrary position, just when reading
2328     * primitive data types (int, char etc).
2329     *
2330     * @param length
2331     *            the number of bytes to skip.
2332     * @return the number of bytes actually skipped.
2333     * @throws IOException
2334     *             if an error occurs while skipping bytes on the source stream.
2335     * @throws NullPointerException
2336     *             if the source stream is {@code null}.
2337     */
2338    public int skipBytes(int length) throws IOException {
2339        // To be used with available. Ok to call if reading primitive buffer
2340        if (input == null) {
2341            throw new NullPointerException();
2342        }
2343
2344        int offset = 0;
2345        while (offset < length) {
2346            checkReadPrimitiveTypes();
2347            long skipped = primitiveData.skip(length - offset);
2348            if (skipped == 0) {
2349                return offset;
2350            }
2351            offset += (int) skipped;
2352        }
2353        return length;
2354    }
2355
2356    /**
2357     * Verify if the SUID & the base name for descriptor
2358     * <code>loadedStreamClass</code>matches
2359     * the SUID & the base name of the corresponding loaded class and
2360     * init private fields.
2361     *
2362     * @param loadedStreamClass
2363     *            An ObjectStreamClass that was loaded from the stream.
2364     *
2365     * @throws InvalidClassException
2366     *             If the SUID of the stream class does not match the VM class
2367     */
2368    private void verifyAndInit(ObjectStreamClass loadedStreamClass)
2369            throws InvalidClassException {
2370
2371        Class<?> localClass = loadedStreamClass.forClass();
2372        ObjectStreamClass localStreamClass = ObjectStreamClass
2373                .lookupStreamClass(localClass);
2374
2375        if (loadedStreamClass.getSerialVersionUID() != localStreamClass
2376                .getSerialVersionUID()) {
2377            throw new InvalidClassException(loadedStreamClass.getName(),
2378                    "Incompatible class (SUID): " + loadedStreamClass +
2379                            " but expected " + localStreamClass);
2380        }
2381
2382        String loadedClassBaseName = getBaseName(loadedStreamClass.getName());
2383        String localClassBaseName = getBaseName(localStreamClass.getName());
2384
2385        if (!loadedClassBaseName.equals(localClassBaseName)) {
2386            throw new InvalidClassException(loadedStreamClass.getName(),
2387                    String.format("Incompatible class (base name): %s but expected %s",
2388                            loadedClassBaseName, localClassBaseName));
2389        }
2390
2391        loadedStreamClass.initPrivateFields(localStreamClass);
2392    }
2393
2394    private static String getBaseName(String fullName) {
2395        int k = fullName.lastIndexOf('.');
2396
2397        if (k == -1 || k == (fullName.length() - 1)) {
2398            return fullName;
2399        }
2400        return fullName.substring(k + 1);
2401    }
2402
2403    // Avoid recursive defining.
2404    private static void checkedSetSuperClassDesc(ObjectStreamClass desc,
2405            ObjectStreamClass superDesc) throws StreamCorruptedException {
2406        if (desc.equals(superDesc)) {
2407            throw new StreamCorruptedException();
2408        }
2409        desc.setSuperclass(superDesc);
2410    }
2411}
2412