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