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