jaroslav@601: /* jaroslav@601: * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. jaroslav@601: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@601: * jaroslav@601: * This code is free software; you can redistribute it and/or modify it jaroslav@601: * under the terms of the GNU General Public License version 2 only, as jaroslav@601: * published by the Free Software Foundation. Oracle designates this jaroslav@601: * particular file as subject to the "Classpath" exception as provided jaroslav@601: * by Oracle in the LICENSE file that accompanied this code. jaroslav@601: * jaroslav@601: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@601: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@601: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@601: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@601: * accompanied this code). jaroslav@601: * jaroslav@601: * You should have received a copy of the GNU General Public License version jaroslav@601: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@601: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@601: * jaroslav@601: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@601: * or visit www.oracle.com if you need additional information or have any jaroslav@601: * questions. jaroslav@601: */ jaroslav@601: jaroslav@601: package java.io; jaroslav@601: jaroslav@601: import java.lang.reflect.Array; jaroslav@601: import java.lang.reflect.Modifier; jaroslav@601: import java.lang.reflect.Proxy; jaroslav@601: import java.util.Arrays; jaroslav@601: import java.util.HashMap; jaroslav@604: import org.apidesign.bck2brwsr.emul.lang.System; jaroslav@601: jaroslav@601: /** jaroslav@601: * An ObjectInputStream deserializes primitive data and objects previously jaroslav@601: * written using an ObjectOutputStream. jaroslav@601: * jaroslav@601: *

ObjectOutputStream and ObjectInputStream can provide an application with jaroslav@601: * persistent storage for graphs of objects when used with a FileOutputStream jaroslav@601: * and FileInputStream respectively. ObjectInputStream is used to recover jaroslav@601: * those objects previously serialized. Other uses include passing objects jaroslav@601: * between hosts using a socket stream or for marshaling and unmarshaling jaroslav@601: * arguments and parameters in a remote communication system. jaroslav@601: * jaroslav@601: *

ObjectInputStream ensures that the types of all objects in the graph jaroslav@601: * created from the stream match the classes present in the Java Virtual jaroslav@601: * Machine. Classes are loaded as required using the standard mechanisms. jaroslav@601: * jaroslav@601: *

Only objects that support the java.io.Serializable or jaroslav@601: * java.io.Externalizable interface can be read from streams. jaroslav@601: * jaroslav@601: *

The method readObject is used to read an object from the jaroslav@601: * stream. Java's safe casting should be used to get the desired type. In jaroslav@601: * Java, strings and arrays are objects and are treated as objects during jaroslav@601: * serialization. When read they need to be cast to the expected type. jaroslav@601: * jaroslav@601: *

Primitive data types can be read from the stream using the appropriate jaroslav@601: * method on DataInput. jaroslav@601: * jaroslav@601: *

The default deserialization mechanism for objects restores the contents jaroslav@601: * of each field to the value and type it had when it was written. Fields jaroslav@601: * declared as transient or static are ignored by the deserialization process. jaroslav@601: * References to other objects cause those objects to be read from the stream jaroslav@601: * as necessary. Graphs of objects are restored correctly using a reference jaroslav@601: * sharing mechanism. New objects are always allocated when deserializing, jaroslav@601: * which prevents existing objects from being overwritten. jaroslav@601: * jaroslav@601: *

Reading an object is analogous to running the constructors of a new jaroslav@601: * object. Memory is allocated for the object and initialized to zero (NULL). jaroslav@601: * No-arg constructors are invoked for the non-serializable classes and then jaroslav@601: * the fields of the serializable classes are restored from the stream starting jaroslav@601: * with the serializable class closest to java.lang.object and finishing with jaroslav@601: * the object's most specific class. jaroslav@601: * jaroslav@601: *

For example to read from a stream as written by the example in jaroslav@601: * ObjectOutputStream: jaroslav@601: *
jaroslav@601: *

jaroslav@601:  *      FileInputStream fis = new FileInputStream("t.tmp");
jaroslav@601:  *      ObjectInputStream ois = new ObjectInputStream(fis);
jaroslav@601:  *
jaroslav@601:  *      int i = ois.readInt();
jaroslav@601:  *      String today = (String) ois.readObject();
jaroslav@601:  *      Date date = (Date) ois.readObject();
jaroslav@601:  *
jaroslav@601:  *      ois.close();
jaroslav@601:  * 
jaroslav@601: * jaroslav@601: *

Classes control how they are serialized by implementing either the jaroslav@601: * java.io.Serializable or java.io.Externalizable interfaces. jaroslav@601: * jaroslav@601: *

Implementing the Serializable interface allows object serialization to jaroslav@601: * save and restore the entire state of the object and it allows classes to jaroslav@601: * evolve between the time the stream is written and the time it is read. It jaroslav@601: * automatically traverses references between objects, saving and restoring jaroslav@601: * entire graphs. jaroslav@601: * jaroslav@601: *

Serializable classes that require special handling during the jaroslav@601: * serialization and deserialization process should implement the following jaroslav@601: * methods:

jaroslav@601: * jaroslav@601: *

jaroslav@601:  * private void writeObject(java.io.ObjectOutputStream stream)
jaroslav@601:  *     throws IOException;
jaroslav@601:  * private void readObject(java.io.ObjectInputStream stream)
jaroslav@601:  *     throws IOException, ClassNotFoundException;
jaroslav@601:  * private void readObjectNoData()
jaroslav@601:  *     throws ObjectStreamException;
jaroslav@601:  * 
jaroslav@601: * jaroslav@601: *

The readObject method is responsible for reading and restoring the state jaroslav@601: * of the object for its particular class using data written to the stream by jaroslav@601: * the corresponding writeObject method. The method does not need to concern jaroslav@601: * itself with the state belonging to its superclasses or subclasses. State is jaroslav@601: * restored by reading data from the ObjectInputStream for the individual jaroslav@601: * fields and making assignments to the appropriate fields of the object. jaroslav@601: * Reading primitive data types is supported by DataInput. jaroslav@601: * jaroslav@601: *

Any attempt to read object data which exceeds the boundaries of the jaroslav@601: * custom data written by the corresponding writeObject method will cause an jaroslav@601: * OptionalDataException to be thrown with an eof field value of true. jaroslav@601: * Non-object reads which exceed the end of the allotted data will reflect the jaroslav@601: * end of data in the same way that they would indicate the end of the stream: jaroslav@601: * bytewise reads will return -1 as the byte read or number of bytes read, and jaroslav@601: * primitive reads will throw EOFExceptions. If there is no corresponding jaroslav@601: * writeObject method, then the end of default serialized data marks the end of jaroslav@601: * the allotted data. jaroslav@601: * jaroslav@601: *

Primitive and object read calls issued from within a readExternal method jaroslav@601: * behave in the same manner--if the stream is already positioned at the end of jaroslav@601: * data written by the corresponding writeExternal method, object reads will jaroslav@601: * throw OptionalDataExceptions with eof set to true, bytewise reads will jaroslav@601: * return -1, and primitive reads will throw EOFExceptions. Note that this jaroslav@601: * behavior does not hold for streams written with the old jaroslav@601: * ObjectStreamConstants.PROTOCOL_VERSION_1 protocol, in which the jaroslav@601: * end of data written by writeExternal methods is not demarcated, and hence jaroslav@601: * cannot be detected. jaroslav@601: * jaroslav@601: *

The readObjectNoData method is responsible for initializing the state of jaroslav@601: * the object for its particular class in the event that the serialization jaroslav@601: * stream does not list the given class as a superclass of the object being jaroslav@601: * deserialized. This may occur in cases where the receiving party uses a jaroslav@601: * different version of the deserialized instance's class than the sending jaroslav@601: * party, and the receiver's version extends classes that are not extended by jaroslav@601: * the sender's version. This may also occur if the serialization stream has jaroslav@601: * been tampered; hence, readObjectNoData is useful for initializing jaroslav@601: * deserialized objects properly despite a "hostile" or incomplete source jaroslav@601: * stream. jaroslav@601: * jaroslav@601: *

Serialization does not read or assign values to the fields of any object jaroslav@601: * that does not implement the java.io.Serializable interface. Subclasses of jaroslav@601: * Objects that are not serializable can be serializable. In this case the jaroslav@601: * non-serializable class must have a no-arg constructor to allow its fields to jaroslav@601: * be initialized. In this case it is the responsibility of the subclass to jaroslav@601: * save and restore the state of the non-serializable class. It is frequently jaroslav@601: * the case that the fields of that class are accessible (public, package, or jaroslav@601: * protected) or that there are get and set methods that can be used to restore jaroslav@601: * the state. jaroslav@601: * jaroslav@601: *

Any exception that occurs while deserializing an object will be caught by jaroslav@601: * the ObjectInputStream and abort the reading process. jaroslav@601: * jaroslav@601: *

Implementing the Externalizable interface allows the object to assume jaroslav@601: * complete control over the contents and format of the object's serialized jaroslav@601: * form. The methods of the Externalizable interface, writeExternal and jaroslav@601: * readExternal, are called to save and restore the objects state. When jaroslav@601: * implemented by a class they can write and read their own state using all of jaroslav@601: * the methods of ObjectOutput and ObjectInput. It is the responsibility of jaroslav@601: * the objects to handle any versioning that occurs. jaroslav@601: * jaroslav@601: *

Enum constants are deserialized differently than ordinary serializable or jaroslav@601: * externalizable objects. The serialized form of an enum constant consists jaroslav@601: * solely of its name; field values of the constant are not transmitted. To jaroslav@601: * deserialize an enum constant, ObjectInputStream reads the constant name from jaroslav@601: * the stream; the deserialized constant is then obtained by calling the static jaroslav@601: * method Enum.valueOf(Class, String) with the enum constant's jaroslav@601: * base type and the received constant name as arguments. Like other jaroslav@601: * serializable or externalizable objects, enum constants can function as the jaroslav@601: * targets of back references appearing subsequently in the serialization jaroslav@601: * stream. The process by which enum constants are deserialized cannot be jaroslav@601: * customized: any class-specific readObject, readObjectNoData, and readResolve jaroslav@601: * methods defined by enum types are ignored during deserialization. jaroslav@601: * Similarly, any serialPersistentFields or serialVersionUID field declarations jaroslav@601: * are also ignored--all enum types have a fixed serialVersionUID of 0L. jaroslav@601: * jaroslav@601: * @author Mike Warres jaroslav@601: * @author Roger Riggs jaroslav@601: * @see java.io.DataInput jaroslav@601: * @see java.io.ObjectOutputStream jaroslav@601: * @see java.io.Serializable jaroslav@601: * @see Object Serialization Specification, Section 3, Object Input Classes jaroslav@601: * @since JDK1.1 jaroslav@601: */ jaroslav@601: public class ObjectInputStream jaroslav@601: extends InputStream implements ObjectInput, ObjectStreamConstants jaroslav@601: { jaroslav@601: /** handle value representing null */ jaroslav@601: private static final int NULL_HANDLE = -1; jaroslav@601: jaroslav@601: /** marker for unshared objects in internal handle table */ jaroslav@601: private static final Object unsharedMarker = new Object(); jaroslav@601: jaroslav@601: /** table mapping primitive type names to corresponding class objects */ jaroslav@601: private static final HashMap> primClasses jaroslav@601: = new HashMap<>(8, 1.0F); jaroslav@601: static { jaroslav@601: primClasses.put("boolean", boolean.class); jaroslav@601: primClasses.put("byte", byte.class); jaroslav@601: primClasses.put("char", char.class); jaroslav@601: primClasses.put("short", short.class); jaroslav@601: primClasses.put("int", int.class); jaroslav@601: primClasses.put("long", long.class); jaroslav@601: primClasses.put("float", float.class); jaroslav@601: primClasses.put("double", double.class); jaroslav@601: primClasses.put("void", void.class); jaroslav@601: } jaroslav@601: jaroslav@601: /** filter stream for handling block data conversion */ jaroslav@601: private final BlockDataInputStream bin; jaroslav@601: /** validation callback list */ jaroslav@601: private final ValidationList vlist; jaroslav@601: /** recursion depth */ jaroslav@601: private int depth; jaroslav@601: /** whether stream is closed */ jaroslav@601: private boolean closed; jaroslav@601: jaroslav@601: /** wire handle -> obj/exception map */ jaroslav@601: private final HandleTable handles; jaroslav@601: /** scratch field for passing handle values up/down call stack */ jaroslav@601: private int passHandle = NULL_HANDLE; jaroslav@601: /** flag set when at end of field value block with no TC_ENDBLOCKDATA */ jaroslav@601: private boolean defaultDataEnd = false; jaroslav@601: jaroslav@601: /** buffer for reading primitive field values */ jaroslav@601: private byte[] primVals; jaroslav@601: jaroslav@601: /** if true, invoke readObjectOverride() instead of readObject() */ jaroslav@601: private final boolean enableOverride; jaroslav@601: /** if true, invoke resolveObject() */ jaroslav@601: private boolean enableResolve; jaroslav@601: jaroslav@601: /** jaroslav@601: * Context during upcalls to class-defined readObject methods; holds jaroslav@601: * object currently being deserialized and descriptor for current class. jaroslav@601: * Null when not during readObject upcall. jaroslav@601: */ jaroslav@604: private Object curContext; jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates an ObjectInputStream that reads from the specified InputStream. jaroslav@601: * A serialization stream header is read from the stream and verified. jaroslav@601: * This constructor will block until the corresponding ObjectOutputStream jaroslav@601: * has written and flushed the header. jaroslav@601: * jaroslav@601: *

If a security manager is installed, this constructor will check for jaroslav@601: * the "enableSubclassImplementation" SerializablePermission when invoked jaroslav@601: * directly or indirectly by the constructor of a subclass which overrides jaroslav@601: * the ObjectInputStream.readFields or ObjectInputStream.readUnshared jaroslav@601: * methods. jaroslav@601: * jaroslav@601: * @param in input stream to read from jaroslav@601: * @throws StreamCorruptedException if the stream header is incorrect jaroslav@601: * @throws IOException if an I/O error occurs while reading stream header jaroslav@601: * @throws SecurityException if untrusted subclass illegally overrides jaroslav@601: * security-sensitive methods jaroslav@601: * @throws NullPointerException if in is null jaroslav@601: * @see ObjectInputStream#ObjectInputStream() jaroslav@601: * @see ObjectInputStream#readFields() jaroslav@601: * @see ObjectOutputStream#ObjectOutputStream(OutputStream) jaroslav@601: */ jaroslav@601: public ObjectInputStream(InputStream in) throws IOException { jaroslav@601: verifySubclass(); jaroslav@601: bin = new BlockDataInputStream(in); jaroslav@601: handles = new HandleTable(10); jaroslav@601: vlist = new ValidationList(); jaroslav@601: enableOverride = false; jaroslav@601: readStreamHeader(); jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Provide a way for subclasses that are completely reimplementing jaroslav@601: * ObjectInputStream to not have to allocate private data just used by this jaroslav@601: * implementation of ObjectInputStream. jaroslav@601: * jaroslav@601: *

If there is a security manager installed, this method first calls the jaroslav@601: * security manager's checkPermission method with the jaroslav@601: * SerializablePermission("enableSubclassImplementation") jaroslav@601: * permission to ensure it's ok to enable subclassing. jaroslav@601: * jaroslav@601: * @throws SecurityException if a security manager exists and its jaroslav@601: * checkPermission method denies enabling jaroslav@601: * subclassing. jaroslav@601: * @see SecurityManager#checkPermission jaroslav@601: * @see java.io.SerializablePermission jaroslav@601: */ jaroslav@601: protected ObjectInputStream() throws IOException, SecurityException { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Read an object from the ObjectInputStream. The class of the object, the jaroslav@601: * signature of the class, and the values of the non-transient and jaroslav@601: * non-static fields of the class and all of its supertypes are read. jaroslav@601: * Default deserializing for a class can be overriden using the writeObject jaroslav@601: * and readObject methods. Objects referenced by this object are read jaroslav@601: * transitively so that a complete equivalent graph of objects is jaroslav@601: * reconstructed by readObject. jaroslav@601: * jaroslav@601: *

The root object is completely restored when all of its fields and the jaroslav@601: * objects it references are completely restored. At this point the object jaroslav@601: * validation callbacks are executed in order based on their registered jaroslav@601: * priorities. The callbacks are registered by objects (in the readObject jaroslav@601: * special methods) as they are individually restored. jaroslav@601: * jaroslav@601: *

Exceptions are thrown for problems with the InputStream and for jaroslav@601: * classes that should not be deserialized. All exceptions are fatal to jaroslav@601: * the InputStream and leave it in an indeterminate state; it is up to the jaroslav@601: * caller to ignore or recover the stream state. jaroslav@601: * jaroslav@601: * @throws ClassNotFoundException Class of a serialized object cannot be jaroslav@601: * found. jaroslav@601: * @throws InvalidClassException Something is wrong with a class used by jaroslav@601: * serialization. jaroslav@601: * @throws StreamCorruptedException Control information in the jaroslav@601: * stream is inconsistent. jaroslav@601: * @throws OptionalDataException Primitive data was found in the jaroslav@601: * stream instead of objects. jaroslav@601: * @throws IOException Any of the usual Input/Output related exceptions. jaroslav@601: */ jaroslav@601: public final Object readObject() jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@604: throw new IOException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * This method is called by trusted subclasses of ObjectOutputStream that jaroslav@601: * constructed ObjectOutputStream using the protected no-arg constructor. jaroslav@601: * The subclass is expected to provide an override method with the modifier jaroslav@601: * "final". jaroslav@601: * jaroslav@601: * @return the Object read from the stream. jaroslav@601: * @throws ClassNotFoundException Class definition of a serialized object jaroslav@601: * cannot be found. jaroslav@601: * @throws OptionalDataException Primitive data was found in the stream jaroslav@601: * instead of objects. jaroslav@601: * @throws IOException if I/O errors occurred while reading from the jaroslav@601: * underlying stream jaroslav@601: * @see #ObjectInputStream() jaroslav@601: * @see #readObject() jaroslav@601: * @since 1.2 jaroslav@601: */ jaroslav@601: protected Object readObjectOverride() jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: return null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads an "unshared" object from the ObjectInputStream. This method is jaroslav@601: * identical to readObject, except that it prevents subsequent calls to jaroslav@601: * readObject and readUnshared from returning additional references to the jaroslav@601: * deserialized instance obtained via this call. Specifically: jaroslav@601: *

jaroslav@601: * Deserializing an object via readUnshared invalidates the stream handle jaroslav@601: * associated with the returned object. Note that this in itself does not jaroslav@601: * always guarantee that the reference returned by readUnshared is unique; jaroslav@601: * the deserialized object may define a readResolve method which returns an jaroslav@601: * object visible to other parties, or readUnshared may return a Class jaroslav@601: * object or enum constant obtainable elsewhere in the stream or through jaroslav@601: * external means. If the deserialized object defines a readResolve method jaroslav@601: * and the invocation of that method returns an array, then readUnshared jaroslav@601: * returns a shallow clone of that array; this guarantees that the returned jaroslav@601: * array object is unique and cannot be obtained a second time from an jaroslav@601: * invocation of readObject or readUnshared on the ObjectInputStream, jaroslav@601: * even if the underlying data stream has been manipulated. jaroslav@601: * jaroslav@601: *

ObjectInputStream subclasses which override this method can only be jaroslav@601: * constructed in security contexts possessing the jaroslav@601: * "enableSubclassImplementation" SerializablePermission; any attempt to jaroslav@601: * instantiate such a subclass without this permission will cause a jaroslav@601: * SecurityException to be thrown. jaroslav@601: * jaroslav@601: * @return reference to deserialized object jaroslav@601: * @throws ClassNotFoundException if class of an object to deserialize jaroslav@601: * cannot be found jaroslav@601: * @throws StreamCorruptedException if control information in the stream jaroslav@601: * is inconsistent jaroslav@601: * @throws ObjectStreamException if object to deserialize has already jaroslav@601: * appeared in stream jaroslav@601: * @throws OptionalDataException if primitive data is next in stream jaroslav@601: * @throws IOException if an I/O error occurs during deserialization jaroslav@601: * @since 1.4 jaroslav@601: */ jaroslav@601: public Object readUnshared() throws IOException, ClassNotFoundException { jaroslav@601: // if nested read, passHandle contains handle of enclosing object jaroslav@601: int outerHandle = passHandle; jaroslav@601: try { jaroslav@601: Object obj = readObject0(true); jaroslav@601: handles.markDependency(outerHandle, passHandle); jaroslav@601: ClassNotFoundException ex = handles.lookupException(passHandle); jaroslav@601: if (ex != null) { jaroslav@601: throw ex; jaroslav@601: } jaroslav@601: if (depth == 0) { jaroslav@601: vlist.doCallbacks(); jaroslav@601: } jaroslav@601: return obj; jaroslav@601: } finally { jaroslav@601: passHandle = outerHandle; jaroslav@601: if (closed && depth == 0) { jaroslav@601: clear(); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Read the non-static and non-transient fields of the current class from jaroslav@601: * this stream. This may only be called from the readObject method of the jaroslav@601: * class being deserialized. It will throw the NotActiveException if it is jaroslav@601: * called otherwise. jaroslav@601: * jaroslav@601: * @throws ClassNotFoundException if the class of a serialized object jaroslav@601: * could not be found. jaroslav@601: * @throws IOException if an I/O error occurs. jaroslav@601: * @throws NotActiveException if the stream is not currently reading jaroslav@601: * objects. jaroslav@601: */ jaroslav@601: public void defaultReadObject() jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: if (curContext == null) { jaroslav@601: throw new NotActiveException("not in call to readObject"); jaroslav@601: } jaroslav@604: Object curObj = null; // curContext.getObj(); jaroslav@604: ObjectStreamClass curDesc = null; // curContext.getDesc(); jaroslav@601: bin.setBlockDataMode(false); jaroslav@601: defaultReadFields(curObj, curDesc); jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: if (!curDesc.hasWriteObjectData()) { jaroslav@601: /* jaroslav@601: * Fix for 4360508: since stream does not contain terminating jaroslav@601: * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere jaroslav@601: * knows to simulate end-of-custom-data behavior. jaroslav@601: */ jaroslav@601: defaultDataEnd = true; jaroslav@601: } jaroslav@601: ClassNotFoundException ex = handles.lookupException(passHandle); jaroslav@601: if (ex != null) { jaroslav@601: throw ex; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads the persistent fields from the stream and makes them available by jaroslav@601: * name. jaroslav@601: * jaroslav@601: * @return the GetField object representing the persistent jaroslav@601: * fields of the object being deserialized jaroslav@601: * @throws ClassNotFoundException if the class of a serialized object jaroslav@601: * could not be found. jaroslav@601: * @throws IOException if an I/O error occurs. jaroslav@601: * @throws NotActiveException if the stream is not currently reading jaroslav@601: * objects. jaroslav@601: * @since 1.2 jaroslav@601: */ jaroslav@601: public ObjectInputStream.GetField readFields() jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: if (curContext == null) { jaroslav@601: throw new NotActiveException("not in call to readObject"); jaroslav@601: } jaroslav@604: Object curObj = null; // curContext.getObj(); jaroslav@604: ObjectStreamClass curDesc = null; // curContext.getDesc(); jaroslav@601: bin.setBlockDataMode(false); jaroslav@601: GetFieldImpl getField = new GetFieldImpl(curDesc); jaroslav@601: getField.readFields(); jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: if (!curDesc.hasWriteObjectData()) { jaroslav@601: /* jaroslav@601: * Fix for 4360508: since stream does not contain terminating jaroslav@601: * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere jaroslav@601: * knows to simulate end-of-custom-data behavior. jaroslav@601: */ jaroslav@601: defaultDataEnd = true; jaroslav@601: } jaroslav@601: jaroslav@601: return getField; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Register an object to be validated before the graph is returned. While jaroslav@601: * similar to resolveObject these validations are called after the entire jaroslav@601: * graph has been reconstituted. Typically, a readObject method will jaroslav@601: * register the object with the stream so that when all of the objects are jaroslav@601: * restored a final set of validations can be performed. jaroslav@601: * jaroslav@601: * @param obj the object to receive the validation callback. jaroslav@601: * @param prio controls the order of callbacks;zero is a good default. jaroslav@601: * Use higher numbers to be called back earlier, lower numbers for jaroslav@601: * later callbacks. Within a priority, callbacks are processed in jaroslav@601: * no particular order. jaroslav@601: * @throws NotActiveException The stream is not currently reading objects jaroslav@601: * so it is invalid to register a callback. jaroslav@601: * @throws InvalidObjectException The validation object is null. jaroslav@601: */ jaroslav@601: public void registerValidation(ObjectInputValidation obj, int prio) jaroslav@601: throws NotActiveException, InvalidObjectException jaroslav@601: { jaroslav@601: if (depth == 0) { jaroslav@601: throw new NotActiveException("stream inactive"); jaroslav@601: } jaroslav@601: vlist.register(obj, prio); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Load the local class equivalent of the specified stream class jaroslav@601: * description. Subclasses may implement this method to allow classes to jaroslav@601: * be fetched from an alternate source. jaroslav@601: * jaroslav@601: *

The corresponding method in ObjectOutputStream is jaroslav@601: * annotateClass. This method will be invoked only once for jaroslav@601: * each unique class in the stream. This method can be implemented by jaroslav@601: * subclasses to use an alternate loading mechanism but must return a jaroslav@601: * Class object. Once returned, if the class is not an array jaroslav@601: * class, its serialVersionUID is compared to the serialVersionUID of the jaroslav@601: * serialized class, and if there is a mismatch, the deserialization fails jaroslav@601: * and an {@link InvalidClassException} is thrown. jaroslav@601: * jaroslav@601: *

The default implementation of this method in jaroslav@601: * ObjectInputStream returns the result of calling jaroslav@601: *

jaroslav@601:      *     Class.forName(desc.getName(), false, loader)
jaroslav@601:      * 
jaroslav@601: * where loader is determined as follows: if there is a jaroslav@601: * method on the current thread's stack whose declaring class was jaroslav@601: * defined by a user-defined class loader (and was not a generated to jaroslav@601: * implement reflective invocations), then loader is class jaroslav@601: * loader corresponding to the closest such method to the currently jaroslav@601: * executing frame; otherwise, loader is jaroslav@601: * null. If this call results in a jaroslav@601: * ClassNotFoundException and the name of the passed jaroslav@601: * ObjectStreamClass instance is the Java language keyword jaroslav@601: * for a primitive type or void, then the Class object jaroslav@601: * representing that primitive type or void will be returned jaroslav@601: * (e.g., an ObjectStreamClass with the name jaroslav@601: * "int" will be resolved to Integer.TYPE). jaroslav@601: * Otherwise, the ClassNotFoundException will be thrown to jaroslav@601: * the caller of this method. jaroslav@601: * jaroslav@601: * @param desc an instance of class ObjectStreamClass jaroslav@601: * @return a Class object corresponding to desc jaroslav@601: * @throws IOException any of the usual Input/Output exceptions. jaroslav@601: * @throws ClassNotFoundException if class of a serialized object cannot jaroslav@601: * be found. jaroslav@601: */ jaroslav@601: protected Class resolveClass(ObjectStreamClass desc) jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: String name = desc.getName(); jaroslav@601: try { jaroslav@601: return Class.forName(name, false, latestUserDefinedLoader()); jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: Class cl = primClasses.get(name); jaroslav@601: if (cl != null) { jaroslav@601: return cl; jaroslav@601: } else { jaroslav@601: throw ex; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns a proxy class that implements the interfaces named in a proxy jaroslav@601: * class descriptor; subclasses may implement this method to read custom jaroslav@601: * data from the stream along with the descriptors for dynamic proxy jaroslav@601: * classes, allowing them to use an alternate loading mechanism for the jaroslav@601: * interfaces and the proxy class. jaroslav@601: * jaroslav@601: *

This method is called exactly once for each unique proxy class jaroslav@601: * descriptor in the stream. jaroslav@601: * jaroslav@601: *

The corresponding method in ObjectOutputStream is jaroslav@601: * annotateProxyClass. For a given subclass of jaroslav@601: * ObjectInputStream that overrides this method, the jaroslav@601: * annotateProxyClass method in the corresponding subclass of jaroslav@601: * ObjectOutputStream must write any data or objects read by jaroslav@601: * this method. jaroslav@601: * jaroslav@601: *

The default implementation of this method in jaroslav@601: * ObjectInputStream returns the result of calling jaroslav@601: * Proxy.getProxyClass with the list of Class jaroslav@601: * objects for the interfaces that are named in the interfaces jaroslav@601: * parameter. The Class object for each interface name jaroslav@601: * i is the value returned by calling jaroslav@601: *

jaroslav@601:      *     Class.forName(i, false, loader)
jaroslav@601:      * 
jaroslav@601: * where loader is that of the first non-null jaroslav@601: * class loader up the execution stack, or null if no jaroslav@601: * non-null class loaders are on the stack (the same class jaroslav@601: * loader choice used by the resolveClass method). Unless any jaroslav@601: * of the resolved interfaces are non-public, this same value of jaroslav@601: * loader is also the class loader passed to jaroslav@601: * Proxy.getProxyClass; if non-public interfaces are present, jaroslav@601: * their class loader is passed instead (if more than one non-public jaroslav@601: * interface class loader is encountered, an jaroslav@601: * IllegalAccessError is thrown). jaroslav@601: * If Proxy.getProxyClass throws an jaroslav@601: * IllegalArgumentException, resolveProxyClass jaroslav@601: * will throw a ClassNotFoundException containing the jaroslav@601: * IllegalArgumentException. jaroslav@601: * jaroslav@601: * @param interfaces the list of interface names that were jaroslav@601: * deserialized in the proxy class descriptor jaroslav@601: * @return a proxy class for the specified interfaces jaroslav@601: * @throws IOException any exception thrown by the underlying jaroslav@601: * InputStream jaroslav@601: * @throws ClassNotFoundException if the proxy class or any of the jaroslav@601: * named interfaces could not be found jaroslav@601: * @see ObjectOutputStream#annotateProxyClass(Class) jaroslav@601: * @since 1.3 jaroslav@601: */ jaroslav@601: protected Class resolveProxyClass(String[] interfaces) jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: ClassLoader latestLoader = latestUserDefinedLoader(); jaroslav@601: ClassLoader nonPublicLoader = null; jaroslav@601: boolean hasNonPublicInterface = false; jaroslav@601: jaroslav@601: // define proxy in class loader of non-public interface(s), if any jaroslav@601: Class[] classObjs = new Class[interfaces.length]; jaroslav@601: for (int i = 0; i < interfaces.length; i++) { jaroslav@601: Class cl = Class.forName(interfaces[i], false, latestLoader); jaroslav@601: if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { jaroslav@601: if (hasNonPublicInterface) { jaroslav@601: if (nonPublicLoader != cl.getClassLoader()) { jaroslav@601: throw new IllegalAccessError( jaroslav@601: "conflicting non-public interface class loaders"); jaroslav@601: } jaroslav@601: } else { jaroslav@601: nonPublicLoader = cl.getClassLoader(); jaroslav@601: hasNonPublicInterface = true; jaroslav@601: } jaroslav@601: } jaroslav@601: classObjs[i] = cl; jaroslav@601: } jaroslav@601: try { jaroslav@601: return Proxy.getProxyClass( jaroslav@601: hasNonPublicInterface ? nonPublicLoader : latestLoader, jaroslav@601: classObjs); jaroslav@601: } catch (IllegalArgumentException e) { jaroslav@601: throw new ClassNotFoundException(null, e); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * This method will allow trusted subclasses of ObjectInputStream to jaroslav@601: * substitute one object for another during deserialization. Replacing jaroslav@601: * objects is disabled until enableResolveObject is called. The jaroslav@601: * enableResolveObject method checks that the stream requesting to resolve jaroslav@601: * object can be trusted. Every reference to serializable objects is passed jaroslav@601: * to resolveObject. To insure that the private state of objects is not jaroslav@601: * unintentionally exposed only trusted streams may use resolveObject. jaroslav@601: * jaroslav@601: *

This method is called after an object has been read but before it is jaroslav@601: * returned from readObject. The default resolveObject method just returns jaroslav@601: * the same object. jaroslav@601: * jaroslav@601: *

When a subclass is replacing objects it must insure that the jaroslav@601: * substituted object is compatible with every field where the reference jaroslav@601: * will be stored. Objects whose type is not a subclass of the type of the jaroslav@601: * field or array element abort the serialization by raising an exception jaroslav@601: * and the object is not be stored. jaroslav@601: * jaroslav@601: *

This method is called only once when each object is first jaroslav@601: * encountered. All subsequent references to the object will be redirected jaroslav@601: * to the new object. jaroslav@601: * jaroslav@601: * @param obj object to be substituted jaroslav@601: * @return the substituted object jaroslav@601: * @throws IOException Any of the usual Input/Output exceptions. jaroslav@601: */ jaroslav@601: protected Object resolveObject(Object obj) throws IOException { jaroslav@601: return obj; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Enable the stream to allow objects read from the stream to be replaced. jaroslav@601: * When enabled, the resolveObject method is called for every object being jaroslav@601: * deserialized. jaroslav@601: * jaroslav@601: *

If enable is true, and there is a security manager installed, jaroslav@601: * this method first calls the security manager's jaroslav@601: * checkPermission method with the jaroslav@601: * SerializablePermission("enableSubstitution") permission to jaroslav@601: * ensure it's ok to enable the stream to allow objects read from the jaroslav@601: * stream to be replaced. jaroslav@601: * jaroslav@601: * @param enable true for enabling use of resolveObject for jaroslav@601: * every object being deserialized jaroslav@601: * @return the previous setting before this method was invoked jaroslav@601: * @throws SecurityException if a security manager exists and its jaroslav@601: * checkPermission method denies enabling the stream jaroslav@601: * to allow objects read from the stream to be replaced. jaroslav@601: * @see SecurityManager#checkPermission jaroslav@601: * @see java.io.SerializablePermission jaroslav@601: */ jaroslav@601: protected boolean enableResolveObject(boolean enable) jaroslav@601: throws SecurityException jaroslav@601: { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * The readStreamHeader method is provided to allow subclasses to read and jaroslav@601: * verify their own stream headers. It reads and verifies the magic number jaroslav@601: * and version number. jaroslav@601: * jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws StreamCorruptedException if control information in the stream jaroslav@601: * is inconsistent jaroslav@601: */ jaroslav@601: protected void readStreamHeader() jaroslav@601: throws IOException, StreamCorruptedException jaroslav@601: { jaroslav@601: short s0 = bin.readShort(); jaroslav@601: short s1 = bin.readShort(); jaroslav@601: if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid stream header: %04X%04X", s0, s1)); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Read a class descriptor from the serialization stream. This method is jaroslav@601: * called when the ObjectInputStream expects a class descriptor as the next jaroslav@601: * item in the serialization stream. Subclasses of ObjectInputStream may jaroslav@601: * override this method to read in class descriptors that have been written jaroslav@601: * in non-standard formats (by subclasses of ObjectOutputStream which have jaroslav@601: * overridden the writeClassDescriptor method). By default, jaroslav@601: * this method reads class descriptors according to the format defined in jaroslav@601: * the Object Serialization specification. jaroslav@601: * jaroslav@601: * @return the class descriptor read jaroslav@601: * @throws IOException If an I/O error has occurred. jaroslav@601: * @throws ClassNotFoundException If the Class of a serialized object used jaroslav@601: * in the class descriptor representation cannot be found jaroslav@601: * @see java.io.ObjectOutputStream#writeClassDescriptor(java.io.ObjectStreamClass) jaroslav@601: * @since 1.3 jaroslav@601: */ jaroslav@601: protected ObjectStreamClass readClassDescriptor() jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: ObjectStreamClass desc = new ObjectStreamClass(); jaroslav@601: desc.readNonProxy(this); jaroslav@601: return desc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a byte of data. This method will block if no input is available. jaroslav@601: * jaroslav@601: * @return the byte read, or -1 if the end of the stream is reached. jaroslav@601: * @throws IOException If an I/O error has occurred. jaroslav@601: */ jaroslav@601: public int read() throws IOException { jaroslav@601: return bin.read(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads into an array of bytes. This method will block until some input jaroslav@601: * is available. Consider using java.io.DataInputStream.readFully to read jaroslav@601: * exactly 'length' bytes. jaroslav@601: * jaroslav@601: * @param buf the buffer into which the data is read jaroslav@601: * @param off the start offset of the data jaroslav@601: * @param len the maximum number of bytes read jaroslav@601: * @return the actual number of bytes read, -1 is returned when the end of jaroslav@601: * the stream is reached. jaroslav@601: * @throws IOException If an I/O error has occurred. jaroslav@601: * @see java.io.DataInputStream#readFully(byte[],int,int) jaroslav@601: */ jaroslav@601: public int read(byte[] buf, int off, int len) throws IOException { jaroslav@601: if (buf == null) { jaroslav@601: throw new NullPointerException(); jaroslav@601: } jaroslav@601: int endoff = off + len; jaroslav@601: if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { jaroslav@601: throw new IndexOutOfBoundsException(); jaroslav@601: } jaroslav@601: return bin.read(buf, off, len, false); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the number of bytes that can be read without blocking. jaroslav@601: * jaroslav@601: * @return the number of available bytes. jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: */ jaroslav@601: public int available() throws IOException { jaroslav@601: return bin.available(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Closes the input stream. Must be called to release any resources jaroslav@601: * associated with the stream. jaroslav@601: * jaroslav@601: * @throws IOException If an I/O error has occurred. jaroslav@601: */ jaroslav@601: public void close() throws IOException { jaroslav@601: /* jaroslav@601: * Even if stream already closed, propagate redundant close to jaroslav@601: * underlying stream to stay consistent with previous implementations. jaroslav@601: */ jaroslav@601: closed = true; jaroslav@601: if (depth == 0) { jaroslav@601: clear(); jaroslav@601: } jaroslav@601: bin.close(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in a boolean. jaroslav@601: * jaroslav@601: * @return the boolean read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public boolean readBoolean() throws IOException { jaroslav@601: return bin.readBoolean(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads an 8 bit byte. jaroslav@601: * jaroslav@601: * @return the 8 bit byte read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public byte readByte() throws IOException { jaroslav@601: return bin.readByte(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads an unsigned 8 bit byte. jaroslav@601: * jaroslav@601: * @return the 8 bit byte read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public int readUnsignedByte() throws IOException { jaroslav@601: return bin.readUnsignedByte(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 16 bit char. jaroslav@601: * jaroslav@601: * @return the 16 bit char read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public char readChar() throws IOException { jaroslav@601: return bin.readChar(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 16 bit short. jaroslav@601: * jaroslav@601: * @return the 16 bit short read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public short readShort() throws IOException { jaroslav@601: return bin.readShort(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads an unsigned 16 bit short. jaroslav@601: * jaroslav@601: * @return the 16 bit short read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public int readUnsignedShort() throws IOException { jaroslav@601: return bin.readUnsignedShort(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 32 bit int. jaroslav@601: * jaroslav@601: * @return the 32 bit integer read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public int readInt() throws IOException { jaroslav@601: return bin.readInt(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 64 bit long. jaroslav@601: * jaroslav@601: * @return the read 64 bit long. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public long readLong() throws IOException { jaroslav@601: return bin.readLong(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 32 bit float. jaroslav@601: * jaroslav@601: * @return the 32 bit float read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public float readFloat() throws IOException { jaroslav@601: return bin.readFloat(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a 64 bit double. jaroslav@601: * jaroslav@601: * @return the 64 bit double read. jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public double readDouble() throws IOException { jaroslav@601: return bin.readDouble(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads bytes, blocking until all bytes are read. jaroslav@601: * jaroslav@601: * @param buf the buffer into which the data is read jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public void readFully(byte[] buf) throws IOException { jaroslav@601: bin.readFully(buf, 0, buf.length, false); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads bytes, blocking until all bytes are read. jaroslav@601: * jaroslav@601: * @param buf the buffer into which the data is read jaroslav@601: * @param off the start offset of the data jaroslav@601: * @param len the maximum number of bytes to read jaroslav@601: * @throws EOFException If end of file is reached. jaroslav@601: * @throws IOException If other I/O error has occurred. jaroslav@601: */ jaroslav@601: public void readFully(byte[] buf, int off, int len) throws IOException { jaroslav@601: int endoff = off + len; jaroslav@601: if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { jaroslav@601: throw new IndexOutOfBoundsException(); jaroslav@601: } jaroslav@601: bin.readFully(buf, off, len, false); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Skips bytes. jaroslav@601: * jaroslav@601: * @param len the number of bytes to be skipped jaroslav@601: * @return the actual number of bytes skipped. jaroslav@601: * @throws IOException If an I/O error has occurred. jaroslav@601: */ jaroslav@601: public int skipBytes(int len) throws IOException { jaroslav@601: return bin.skipBytes(len); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in a line that has been terminated by a \n, \r, \r\n or EOF. jaroslav@601: * jaroslav@601: * @return a String copy of the line. jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @deprecated This method does not properly convert bytes to characters. jaroslav@601: * see DataInputStream for the details and alternatives. jaroslav@601: */ jaroslav@601: @Deprecated jaroslav@601: public String readLine() throws IOException { jaroslav@601: return bin.readLine(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads a String in jaroslav@601: * modified UTF-8 jaroslav@601: * format. jaroslav@601: * jaroslav@601: * @return the String. jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws UTFDataFormatException if read bytes do not represent a valid jaroslav@601: * modified UTF-8 encoding of a string jaroslav@601: */ jaroslav@601: public String readUTF() throws IOException { jaroslav@601: return bin.readUTF(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Provide access to the persistent fields read from the input stream. jaroslav@601: */ jaroslav@601: public static abstract class GetField { jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the ObjectStreamClass that describes the fields in the stream. jaroslav@601: * jaroslav@601: * @return the descriptor class that describes the serializable fields jaroslav@601: */ jaroslav@601: public abstract ObjectStreamClass getObjectStreamClass(); jaroslav@601: jaroslav@601: /** jaroslav@601: * Return true if the named field is defaulted and has no value in this jaroslav@601: * stream. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @return true, if and only if the named field is defaulted jaroslav@601: * @throws IOException if there are I/O errors while reading from jaroslav@601: * the underlying InputStream jaroslav@601: * @throws IllegalArgumentException if name does not jaroslav@601: * correspond to a serializable field jaroslav@601: */ jaroslav@601: public abstract boolean defaulted(String name) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named boolean field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named boolean field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract boolean get(String name, boolean val) jaroslav@601: throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named byte field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named byte field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract byte get(String name, byte val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named char field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named char field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract char get(String name, char val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named short field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named short field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract short get(String name, short val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named int field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named int field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract int get(String name, int val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named long field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named long field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract long get(String name, long val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named float field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named float field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract float get(String name, float val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named double field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named double field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract double get(String name, double val) throws IOException; jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the value of the named Object field from the persistent field. jaroslav@601: * jaroslav@601: * @param name the name of the field jaroslav@601: * @param val the default value to use if name does not jaroslav@601: * have a value jaroslav@601: * @return the value of the named Object field jaroslav@601: * @throws IOException if there are I/O errors while reading from the jaroslav@601: * underlying InputStream jaroslav@601: * @throws IllegalArgumentException if type of name is jaroslav@601: * not serializable or if the field type is incorrect jaroslav@601: */ jaroslav@601: public abstract Object get(String name, Object val) throws IOException; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Verifies that this (possibly subclass) instance can be constructed jaroslav@601: * without violating security constraints: the subclass must not override jaroslav@601: * security-sensitive non-final methods, or else the jaroslav@601: * "enableSubclassImplementation" SerializablePermission is checked. jaroslav@601: */ jaroslav@601: private void verifySubclass() { jaroslav@601: Class cl = getClass(); jaroslav@601: if (cl == ObjectInputStream.class) { jaroslav@601: return; jaroslav@601: } jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Clears internal data structures. jaroslav@601: */ jaroslav@601: private void clear() { jaroslav@601: handles.clear(); jaroslav@601: vlist.clear(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Underlying readObject implementation. jaroslav@601: */ jaroslav@601: private Object readObject0(boolean unshared) throws IOException { jaroslav@601: boolean oldMode = bin.getBlockDataMode(); jaroslav@601: if (oldMode) { jaroslav@601: int remain = bin.currentBlockRemaining(); jaroslav@601: if (remain > 0) { jaroslav@601: throw new OptionalDataException(remain); jaroslav@601: } else if (defaultDataEnd) { jaroslav@601: /* jaroslav@601: * Fix for 4360508: stream is currently at the end of a field jaroslav@601: * value block written via default serialization; since there jaroslav@601: * is no terminating TC_ENDBLOCKDATA tag, simulate jaroslav@601: * end-of-custom-data behavior explicitly. jaroslav@601: */ jaroslav@601: throw new OptionalDataException(true); jaroslav@601: } jaroslav@601: bin.setBlockDataMode(false); jaroslav@601: } jaroslav@601: jaroslav@601: byte tc; jaroslav@601: while ((tc = bin.peekByte()) == TC_RESET) { jaroslav@601: bin.readByte(); jaroslav@601: handleReset(); jaroslav@601: } jaroslav@601: jaroslav@601: depth++; jaroslav@601: try { jaroslav@601: switch (tc) { jaroslav@601: case TC_NULL: jaroslav@601: return readNull(); jaroslav@601: jaroslav@601: case TC_REFERENCE: jaroslav@601: return readHandle(unshared); jaroslav@601: jaroslav@601: case TC_CLASS: jaroslav@601: return readClass(unshared); jaroslav@601: jaroslav@601: case TC_CLASSDESC: jaroslav@601: case TC_PROXYCLASSDESC: jaroslav@601: return readClassDesc(unshared); jaroslav@601: jaroslav@601: case TC_STRING: jaroslav@601: case TC_LONGSTRING: jaroslav@601: return checkResolve(readString(unshared)); jaroslav@601: jaroslav@601: case TC_ARRAY: jaroslav@601: return checkResolve(readArray(unshared)); jaroslav@601: jaroslav@601: case TC_ENUM: jaroslav@601: return checkResolve(readEnum(unshared)); jaroslav@601: jaroslav@601: case TC_OBJECT: jaroslav@601: return checkResolve(readOrdinaryObject(unshared)); jaroslav@601: jaroslav@601: case TC_EXCEPTION: jaroslav@601: IOException ex = readFatalException(); jaroslav@601: throw new WriteAbortedException("writing aborted", ex); jaroslav@601: jaroslav@601: case TC_BLOCKDATA: jaroslav@601: case TC_BLOCKDATALONG: jaroslav@601: if (oldMode) { jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: bin.peek(); // force header read jaroslav@601: throw new OptionalDataException( jaroslav@601: bin.currentBlockRemaining()); jaroslav@601: } else { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "unexpected block data"); jaroslav@601: } jaroslav@601: jaroslav@601: case TC_ENDBLOCKDATA: jaroslav@601: if (oldMode) { jaroslav@601: throw new OptionalDataException(true); jaroslav@601: } else { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "unexpected end of block data"); jaroslav@601: } jaroslav@601: jaroslav@601: default: jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid type code: %02X", tc)); jaroslav@601: } jaroslav@601: } finally { jaroslav@601: depth--; jaroslav@601: bin.setBlockDataMode(oldMode); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If resolveObject has been enabled and given object does not have an jaroslav@601: * exception associated with it, calls resolveObject to determine jaroslav@601: * replacement for object, and updates handle table accordingly. Returns jaroslav@601: * replacement object, or echoes provided object if no replacement jaroslav@601: * occurred. Expects that passHandle is set to given object's handle prior jaroslav@601: * to calling this method. jaroslav@601: */ jaroslav@601: private Object checkResolve(Object obj) throws IOException { jaroslav@601: if (!enableResolve || handles.lookupException(passHandle) != null) { jaroslav@601: return obj; jaroslav@601: } jaroslav@601: Object rep = resolveObject(obj); jaroslav@601: if (rep != obj) { jaroslav@601: handles.setObject(passHandle, rep); jaroslav@601: } jaroslav@601: return rep; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads string without allowing it to be replaced in stream. Called from jaroslav@601: * within ObjectStreamClass.read(). jaroslav@601: */ jaroslav@601: String readTypeString() throws IOException { jaroslav@601: int oldHandle = passHandle; jaroslav@601: try { jaroslav@601: byte tc = bin.peekByte(); jaroslav@601: switch (tc) { jaroslav@601: case TC_NULL: jaroslav@601: return (String) readNull(); jaroslav@601: jaroslav@601: case TC_REFERENCE: jaroslav@601: return (String) readHandle(false); jaroslav@601: jaroslav@601: case TC_STRING: jaroslav@601: case TC_LONGSTRING: jaroslav@601: return readString(false); jaroslav@601: jaroslav@601: default: jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid type code: %02X", tc)); jaroslav@601: } jaroslav@601: } finally { jaroslav@601: passHandle = oldHandle; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in null code, sets passHandle to NULL_HANDLE and returns null. jaroslav@601: */ jaroslav@601: private Object readNull() throws IOException { jaroslav@601: if (bin.readByte() != TC_NULL) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: passHandle = NULL_HANDLE; jaroslav@601: return null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in object handle, sets passHandle to the read handle, and returns jaroslav@601: * object associated with the handle. jaroslav@601: */ jaroslav@601: private Object readHandle(boolean unshared) throws IOException { jaroslav@601: if (bin.readByte() != TC_REFERENCE) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: passHandle = bin.readInt() - baseWireHandle; jaroslav@601: if (passHandle < 0 || passHandle >= handles.size()) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid handle value: %08X", passHandle + jaroslav@601: baseWireHandle)); jaroslav@601: } jaroslav@601: if (unshared) { jaroslav@601: // REMIND: what type of exception to throw here? jaroslav@601: throw new InvalidObjectException( jaroslav@601: "cannot read back reference as unshared"); jaroslav@601: } jaroslav@601: jaroslav@601: Object obj = handles.lookupObject(passHandle); jaroslav@601: if (obj == unsharedMarker) { jaroslav@601: // REMIND: what type of exception to throw here? jaroslav@601: throw new InvalidObjectException( jaroslav@601: "cannot read back reference to unshared object"); jaroslav@601: } jaroslav@601: return obj; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns class object. Sets passHandle to class object's jaroslav@601: * assigned handle. Returns null if class is unresolvable (in which case a jaroslav@601: * ClassNotFoundException will be associated with the class' handle in the jaroslav@601: * handle table). jaroslav@601: */ jaroslav@601: private Class readClass(boolean unshared) throws IOException { jaroslav@601: if (bin.readByte() != TC_CLASS) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: ObjectStreamClass desc = readClassDesc(false); jaroslav@601: Class cl = desc.forClass(); jaroslav@601: passHandle = handles.assign(unshared ? unsharedMarker : cl); jaroslav@601: jaroslav@601: ClassNotFoundException resolveEx = desc.getResolveException(); jaroslav@601: if (resolveEx != null) { jaroslav@601: handles.markException(passHandle, resolveEx); jaroslav@601: } jaroslav@601: jaroslav@601: handles.finish(passHandle); jaroslav@601: return cl; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns (possibly null) class descriptor. Sets passHandle jaroslav@601: * to class descriptor's assigned handle. If class descriptor cannot be jaroslav@601: * resolved to a class in the local VM, a ClassNotFoundException is jaroslav@601: * associated with the class descriptor's handle. jaroslav@601: */ jaroslav@601: private ObjectStreamClass readClassDesc(boolean unshared) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: byte tc = bin.peekByte(); jaroslav@601: switch (tc) { jaroslav@601: case TC_NULL: jaroslav@601: return (ObjectStreamClass) readNull(); jaroslav@601: jaroslav@601: case TC_REFERENCE: jaroslav@601: return (ObjectStreamClass) readHandle(unshared); jaroslav@601: jaroslav@601: case TC_PROXYCLASSDESC: jaroslav@601: return readProxyDesc(unshared); jaroslav@601: jaroslav@601: case TC_CLASSDESC: jaroslav@601: return readNonProxyDesc(unshared); jaroslav@601: jaroslav@601: default: jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid type code: %02X", tc)); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns class descriptor for a dynamic proxy class. Sets jaroslav@601: * passHandle to proxy class descriptor's assigned handle. If proxy class jaroslav@601: * descriptor cannot be resolved to a class in the local VM, a jaroslav@601: * ClassNotFoundException is associated with the descriptor's handle. jaroslav@601: */ jaroslav@601: private ObjectStreamClass readProxyDesc(boolean unshared) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: if (bin.readByte() != TC_PROXYCLASSDESC) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: jaroslav@601: ObjectStreamClass desc = new ObjectStreamClass(); jaroslav@601: int descHandle = handles.assign(unshared ? unsharedMarker : desc); jaroslav@601: passHandle = NULL_HANDLE; jaroslav@601: jaroslav@601: int numIfaces = bin.readInt(); jaroslav@601: String[] ifaces = new String[numIfaces]; jaroslav@601: for (int i = 0; i < numIfaces; i++) { jaroslav@601: ifaces[i] = bin.readUTF(); jaroslav@601: } jaroslav@601: jaroslav@601: Class cl = null; jaroslav@601: ClassNotFoundException resolveEx = null; jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: try { jaroslav@601: if ((cl = resolveProxyClass(ifaces)) == null) { jaroslav@601: resolveEx = new ClassNotFoundException("null class"); jaroslav@601: } jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: resolveEx = ex; jaroslav@601: } jaroslav@601: skipCustomData(); jaroslav@601: jaroslav@601: desc.initProxy(cl, resolveEx, readClassDesc(false)); jaroslav@601: jaroslav@601: handles.finish(descHandle); jaroslav@601: passHandle = descHandle; jaroslav@601: return desc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns class descriptor for a class that is not a dynamic jaroslav@601: * proxy class. Sets passHandle to class descriptor's assigned handle. If jaroslav@601: * class descriptor cannot be resolved to a class in the local VM, a jaroslav@601: * ClassNotFoundException is associated with the descriptor's handle. jaroslav@601: */ jaroslav@601: private ObjectStreamClass readNonProxyDesc(boolean unshared) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: if (bin.readByte() != TC_CLASSDESC) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: jaroslav@601: ObjectStreamClass desc = new ObjectStreamClass(); jaroslav@601: int descHandle = handles.assign(unshared ? unsharedMarker : desc); jaroslav@601: passHandle = NULL_HANDLE; jaroslav@601: jaroslav@601: ObjectStreamClass readDesc = null; jaroslav@601: try { jaroslav@601: readDesc = readClassDescriptor(); jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: throw (IOException) new InvalidClassException( jaroslav@601: "failed to read class descriptor").initCause(ex); jaroslav@601: } jaroslav@601: jaroslav@601: Class cl = null; jaroslav@601: ClassNotFoundException resolveEx = null; jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: try { jaroslav@601: if ((cl = resolveClass(readDesc)) == null) { jaroslav@601: resolveEx = new ClassNotFoundException("null class"); jaroslav@601: } jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: resolveEx = ex; jaroslav@601: } jaroslav@601: skipCustomData(); jaroslav@601: jaroslav@601: desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); jaroslav@601: jaroslav@601: handles.finish(descHandle); jaroslav@601: passHandle = descHandle; jaroslav@601: return desc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns new string. Sets passHandle to new string's jaroslav@601: * assigned handle. jaroslav@601: */ jaroslav@601: private String readString(boolean unshared) throws IOException { jaroslav@601: String str; jaroslav@601: byte tc = bin.readByte(); jaroslav@601: switch (tc) { jaroslav@601: case TC_STRING: jaroslav@601: str = bin.readUTF(); jaroslav@601: break; jaroslav@601: jaroslav@601: case TC_LONGSTRING: jaroslav@601: str = bin.readLongUTF(); jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid type code: %02X", tc)); jaroslav@601: } jaroslav@601: passHandle = handles.assign(unshared ? unsharedMarker : str); jaroslav@601: handles.finish(passHandle); jaroslav@601: return str; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns array object, or null if array class is jaroslav@601: * unresolvable. Sets passHandle to array's assigned handle. jaroslav@601: */ jaroslav@601: private Object readArray(boolean unshared) throws IOException { jaroslav@601: if (bin.readByte() != TC_ARRAY) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: jaroslav@601: ObjectStreamClass desc = readClassDesc(false); jaroslav@601: int len = bin.readInt(); jaroslav@601: jaroslav@601: Object array = null; jaroslav@601: Class cl, ccl = null; jaroslav@601: if ((cl = desc.forClass()) != null) { jaroslav@601: ccl = cl.getComponentType(); jaroslav@601: array = Array.newInstance(ccl, len); jaroslav@601: } jaroslav@601: jaroslav@601: int arrayHandle = handles.assign(unshared ? unsharedMarker : array); jaroslav@601: ClassNotFoundException resolveEx = desc.getResolveException(); jaroslav@601: if (resolveEx != null) { jaroslav@601: handles.markException(arrayHandle, resolveEx); jaroslav@601: } jaroslav@601: jaroslav@601: if (ccl == null) { jaroslav@601: for (int i = 0; i < len; i++) { jaroslav@601: readObject0(false); jaroslav@601: } jaroslav@601: } else if (ccl.isPrimitive()) { jaroslav@601: if (ccl == Integer.TYPE) { jaroslav@601: bin.readInts((int[]) array, 0, len); jaroslav@601: } else if (ccl == Byte.TYPE) { jaroslav@601: bin.readFully((byte[]) array, 0, len, true); jaroslav@601: } else if (ccl == Long.TYPE) { jaroslav@601: bin.readLongs((long[]) array, 0, len); jaroslav@601: } else if (ccl == Float.TYPE) { jaroslav@601: bin.readFloats((float[]) array, 0, len); jaroslav@601: } else if (ccl == Double.TYPE) { jaroslav@601: bin.readDoubles((double[]) array, 0, len); jaroslav@601: } else if (ccl == Short.TYPE) { jaroslav@601: bin.readShorts((short[]) array, 0, len); jaroslav@601: } else if (ccl == Character.TYPE) { jaroslav@601: bin.readChars((char[]) array, 0, len); jaroslav@601: } else if (ccl == Boolean.TYPE) { jaroslav@601: bin.readBooleans((boolean[]) array, 0, len); jaroslav@601: } else { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: Object[] oa = (Object[]) array; jaroslav@601: for (int i = 0; i < len; i++) { jaroslav@601: oa[i] = readObject0(false); jaroslav@601: handles.markDependency(arrayHandle, passHandle); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: handles.finish(arrayHandle); jaroslav@601: passHandle = arrayHandle; jaroslav@601: return array; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns enum constant, or null if enum type is jaroslav@601: * unresolvable. Sets passHandle to enum constant's assigned handle. jaroslav@601: */ jaroslav@601: private Enum readEnum(boolean unshared) throws IOException { jaroslav@601: if (bin.readByte() != TC_ENUM) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: jaroslav@601: ObjectStreamClass desc = readClassDesc(false); jaroslav@601: if (!desc.isEnum()) { jaroslav@601: throw new InvalidClassException("non-enum class: " + desc); jaroslav@601: } jaroslav@601: jaroslav@601: int enumHandle = handles.assign(unshared ? unsharedMarker : null); jaroslav@601: ClassNotFoundException resolveEx = desc.getResolveException(); jaroslav@601: if (resolveEx != null) { jaroslav@601: handles.markException(enumHandle, resolveEx); jaroslav@601: } jaroslav@601: jaroslav@601: String name = readString(false); jaroslav@601: Enum en = null; jaroslav@601: Class cl = desc.forClass(); jaroslav@601: if (cl != null) { jaroslav@601: try { jaroslav@601: en = Enum.valueOf(cl, name); jaroslav@601: } catch (IllegalArgumentException ex) { jaroslav@601: throw (IOException) new InvalidObjectException( jaroslav@601: "enum constant " + name + " does not exist in " + jaroslav@601: cl).initCause(ex); jaroslav@601: } jaroslav@601: if (!unshared) { jaroslav@601: handles.setObject(enumHandle, en); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: handles.finish(enumHandle); jaroslav@601: passHandle = enumHandle; jaroslav@601: return en; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads and returns "ordinary" (i.e., not a String, Class, jaroslav@601: * ObjectStreamClass, array, or enum constant) object, or null if object's jaroslav@601: * class is unresolvable (in which case a ClassNotFoundException will be jaroslav@601: * associated with object's handle). Sets passHandle to object's assigned jaroslav@601: * handle. jaroslav@601: */ jaroslav@601: private Object readOrdinaryObject(boolean unshared) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: if (bin.readByte() != TC_OBJECT) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: jaroslav@601: ObjectStreamClass desc = readClassDesc(false); jaroslav@601: desc.checkDeserialize(); jaroslav@601: jaroslav@601: Object obj; jaroslav@601: try { jaroslav@601: obj = desc.isInstantiable() ? desc.newInstance() : null; jaroslav@601: } catch (Exception ex) { jaroslav@601: throw (IOException) new InvalidClassException( jaroslav@601: desc.forClass().getName(), jaroslav@601: "unable to create instance").initCause(ex); jaroslav@601: } jaroslav@601: jaroslav@601: passHandle = handles.assign(unshared ? unsharedMarker : obj); jaroslav@601: ClassNotFoundException resolveEx = desc.getResolveException(); jaroslav@601: if (resolveEx != null) { jaroslav@601: handles.markException(passHandle, resolveEx); jaroslav@601: } jaroslav@601: jaroslav@601: if (desc.isExternalizable()) { jaroslav@601: readExternalData((Externalizable) obj, desc); jaroslav@601: } else { jaroslav@601: readSerialData(obj, desc); jaroslav@601: } jaroslav@601: jaroslav@601: handles.finish(passHandle); jaroslav@601: jaroslav@601: if (obj != null && jaroslav@601: handles.lookupException(passHandle) == null && jaroslav@601: desc.hasReadResolveMethod()) jaroslav@601: { jaroslav@601: Object rep = desc.invokeReadResolve(obj); jaroslav@601: if (unshared && rep.getClass().isArray()) { jaroslav@601: rep = cloneArray(rep); jaroslav@601: } jaroslav@601: if (rep != obj) { jaroslav@601: handles.setObject(passHandle, obj = rep); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: return obj; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If obj is non-null, reads externalizable data by invoking readExternal() jaroslav@601: * method of obj; otherwise, attempts to skip over externalizable data. jaroslav@601: * Expects that passHandle is set to obj's handle before this method is jaroslav@601: * called. jaroslav@601: */ jaroslav@601: private void readExternalData(Externalizable obj, ObjectStreamClass desc) jaroslav@601: throws IOException jaroslav@601: { jaroslav@604: Object oldContext = curContext; jaroslav@601: curContext = null; jaroslav@601: try { jaroslav@601: boolean blocked = desc.hasBlockExternalData(); jaroslav@601: if (blocked) { jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: } jaroslav@601: if (obj != null) { jaroslav@601: try { jaroslav@601: obj.readExternal(this); jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: /* jaroslav@601: * In most cases, the handle table has already propagated jaroslav@601: * a CNFException to passHandle at this point; this mark jaroslav@601: * call is included to address cases where the readExternal jaroslav@601: * method has cons'ed and thrown a new CNFException of its jaroslav@601: * own. jaroslav@601: */ jaroslav@601: handles.markException(passHandle, ex); jaroslav@601: } jaroslav@601: } jaroslav@601: if (blocked) { jaroslav@601: skipCustomData(); jaroslav@601: } jaroslav@601: } finally { jaroslav@601: curContext = oldContext; jaroslav@601: } jaroslav@601: /* jaroslav@601: * At this point, if the externalizable data was not written in jaroslav@601: * block-data form and either the externalizable class doesn't exist jaroslav@601: * locally (i.e., obj == null) or readExternal() just threw a jaroslav@601: * CNFException, then the stream is probably in an inconsistent state, jaroslav@601: * since some (or all) of the externalizable data may not have been jaroslav@601: * consumed. Since there's no "correct" action to take in this case, jaroslav@601: * we mimic the behavior of past serialization implementations and jaroslav@601: * blindly hope that the stream is in sync; if it isn't and additional jaroslav@601: * externalizable data remains in the stream, a subsequent read will jaroslav@601: * most likely throw a StreamCorruptedException. jaroslav@601: */ jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads (or attempts to skip, if obj is null or is tagged with a jaroslav@601: * ClassNotFoundException) instance data for each serializable class of jaroslav@601: * object in stream, from superclass to subclass. Expects that passHandle jaroslav@601: * is set to obj's handle before this method is called. jaroslav@601: */ jaroslav@601: private void readSerialData(Object obj, ObjectStreamClass desc) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); jaroslav@601: for (int i = 0; i < slots.length; i++) { jaroslav@601: ObjectStreamClass slotDesc = slots[i].desc; jaroslav@601: jaroslav@601: if (slots[i].hasData) { jaroslav@601: if (obj != null && jaroslav@601: slotDesc.hasReadObjectMethod() && jaroslav@601: handles.lookupException(passHandle) == null) jaroslav@601: { jaroslav@604: Object oldContext = curContext; jaroslav@601: jaroslav@601: try { jaroslav@604: curContext = null; //new SerialCallbackContext(obj, slotDesc); jaroslav@601: jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: slotDesc.invokeReadObject(obj, this); jaroslav@601: } catch (ClassNotFoundException ex) { jaroslav@601: /* jaroslav@601: * In most cases, the handle table has already jaroslav@601: * propagated a CNFException to passHandle at this jaroslav@601: * point; this mark call is included to address cases jaroslav@601: * where the custom readObject method has cons'ed and jaroslav@601: * thrown a new CNFException of its own. jaroslav@601: */ jaroslav@601: handles.markException(passHandle, ex); jaroslav@601: } finally { jaroslav@604: //curContext.setUsed(); jaroslav@601: curContext = oldContext; jaroslav@601: } jaroslav@601: jaroslav@601: /* jaroslav@601: * defaultDataEnd may have been set indirectly by custom jaroslav@601: * readObject() method when calling defaultReadObject() or jaroslav@601: * readFields(); clear it to restore normal read behavior. jaroslav@601: */ jaroslav@601: defaultDataEnd = false; jaroslav@601: } else { jaroslav@601: defaultReadFields(obj, slotDesc); jaroslav@601: } jaroslav@601: if (slotDesc.hasWriteObjectData()) { jaroslav@601: skipCustomData(); jaroslav@601: } else { jaroslav@601: bin.setBlockDataMode(false); jaroslav@601: } jaroslav@601: } else { jaroslav@601: if (obj != null && jaroslav@601: slotDesc.hasReadObjectNoDataMethod() && jaroslav@601: handles.lookupException(passHandle) == null) jaroslav@601: { jaroslav@601: slotDesc.invokeReadObjectNoData(obj); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Skips over all block data and objects until TC_ENDBLOCKDATA is jaroslav@601: * encountered. jaroslav@601: */ jaroslav@601: private void skipCustomData() throws IOException { jaroslav@601: int oldHandle = passHandle; jaroslav@601: for (;;) { jaroslav@601: if (bin.getBlockDataMode()) { jaroslav@601: bin.skipBlockData(); jaroslav@601: bin.setBlockDataMode(false); jaroslav@601: } jaroslav@601: switch (bin.peekByte()) { jaroslav@601: case TC_BLOCKDATA: jaroslav@601: case TC_BLOCKDATALONG: jaroslav@601: bin.setBlockDataMode(true); jaroslav@601: break; jaroslav@601: jaroslav@601: case TC_ENDBLOCKDATA: jaroslav@601: bin.readByte(); jaroslav@601: passHandle = oldHandle; jaroslav@601: return; jaroslav@601: jaroslav@601: default: jaroslav@601: readObject0(false); jaroslav@601: break; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in values of serializable fields declared by given class jaroslav@601: * descriptor. If obj is non-null, sets field values in obj. Expects that jaroslav@601: * passHandle is set to obj's handle before this method is called. jaroslav@601: */ jaroslav@601: private void defaultReadFields(Object obj, ObjectStreamClass desc) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: // REMIND: is isInstance check necessary? jaroslav@601: Class cl = desc.forClass(); jaroslav@601: if (cl != null && obj != null && !cl.isInstance(obj)) { jaroslav@601: throw new ClassCastException(); jaroslav@601: } jaroslav@601: jaroslav@601: int primDataSize = desc.getPrimDataSize(); jaroslav@601: if (primVals == null || primVals.length < primDataSize) { jaroslav@601: primVals = new byte[primDataSize]; jaroslav@601: } jaroslav@601: bin.readFully(primVals, 0, primDataSize, false); jaroslav@601: if (obj != null) { jaroslav@601: desc.setPrimFieldValues(obj, primVals); jaroslav@601: } jaroslav@601: jaroslav@601: int objHandle = passHandle; jaroslav@601: ObjectStreamField[] fields = desc.getFields(false); jaroslav@601: Object[] objVals = new Object[desc.getNumObjFields()]; jaroslav@601: int numPrimFields = fields.length - objVals.length; jaroslav@601: for (int i = 0; i < objVals.length; i++) { jaroslav@601: ObjectStreamField f = fields[numPrimFields + i]; jaroslav@601: objVals[i] = readObject0(f.isUnshared()); jaroslav@601: if (f.getField() != null) { jaroslav@601: handles.markDependency(objHandle, passHandle); jaroslav@601: } jaroslav@601: } jaroslav@601: if (obj != null) { jaroslav@601: desc.setObjFieldValues(obj, objVals); jaroslav@601: } jaroslav@601: passHandle = objHandle; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in and returns IOException that caused serialization to abort. jaroslav@601: * All stream state is discarded prior to reading in fatal exception. Sets jaroslav@601: * passHandle to fatal exception's handle. jaroslav@601: */ jaroslav@601: private IOException readFatalException() throws IOException { jaroslav@601: if (bin.readByte() != TC_EXCEPTION) { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: clear(); jaroslav@601: return (IOException) readObject0(false); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If recursion depth is 0, clears internal data structures; otherwise, jaroslav@601: * throws a StreamCorruptedException. This method is called when a jaroslav@601: * TC_RESET typecode is encountered. jaroslav@601: */ jaroslav@601: private void handleReset() throws StreamCorruptedException { jaroslav@601: if (depth > 0) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "unexpected reset; recursion depth: " + depth); jaroslav@601: } jaroslav@601: clear(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Converts specified span of bytes into float values. jaroslav@601: */ jaroslav@601: // REMIND: remove once hotspot inlines Float.intBitsToFloat jaroslav@601: private static native void bytesToFloats(byte[] src, int srcpos, jaroslav@601: float[] dst, int dstpos, jaroslav@601: int nfloats); jaroslav@601: jaroslav@601: /** jaroslav@601: * Converts specified span of bytes into double values. jaroslav@601: */ jaroslav@601: // REMIND: remove once hotspot inlines Double.longBitsToDouble jaroslav@601: private static native void bytesToDoubles(byte[] src, int srcpos, jaroslav@601: double[] dst, int dstpos, jaroslav@601: int ndoubles); jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the first non-null class loader (not counting class loaders of jaroslav@601: * generated reflection implementation classes) up the execution stack, or jaroslav@601: * null if only code from the null class loader is on the stack. This jaroslav@601: * method is also called via reflection by the following RMI-IIOP class: jaroslav@601: * jaroslav@601: * com.sun.corba.se.internal.util.JDKClassLoader jaroslav@601: * jaroslav@601: * This method should not be removed or its signature changed without jaroslav@601: * corresponding modifications to the above class. jaroslav@601: */ jaroslav@601: // REMIND: change name to something more accurate? jaroslav@601: private static native ClassLoader latestUserDefinedLoader(); jaroslav@601: jaroslav@601: /** jaroslav@601: * Default GetField implementation. jaroslav@601: */ jaroslav@601: private class GetFieldImpl extends GetField { jaroslav@601: jaroslav@601: /** class descriptor describing serializable fields */ jaroslav@601: private final ObjectStreamClass desc; jaroslav@601: /** primitive field values */ jaroslav@601: private final byte[] primVals; jaroslav@601: /** object field values */ jaroslav@601: private final Object[] objVals; jaroslav@601: /** object field value handles */ jaroslav@601: private final int[] objHandles; jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates GetFieldImpl object for reading fields defined in given jaroslav@601: * class descriptor. jaroslav@601: */ jaroslav@601: GetFieldImpl(ObjectStreamClass desc) { jaroslav@601: this.desc = desc; jaroslav@601: primVals = new byte[desc.getPrimDataSize()]; jaroslav@601: objVals = new Object[desc.getNumObjFields()]; jaroslav@601: objHandles = new int[objVals.length]; jaroslav@601: } jaroslav@601: jaroslav@601: public ObjectStreamClass getObjectStreamClass() { jaroslav@601: return desc; jaroslav@601: } jaroslav@601: jaroslav@601: public boolean defaulted(String name) throws IOException { jaroslav@601: return (getFieldOffset(name, null) < 0); jaroslav@601: } jaroslav@601: jaroslav@601: public boolean get(String name, boolean val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Boolean.TYPE); jaroslav@601: return (off >= 0) ? Bits.getBoolean(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public byte get(String name, byte val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Byte.TYPE); jaroslav@601: return (off >= 0) ? primVals[off] : val; jaroslav@601: } jaroslav@601: jaroslav@601: public char get(String name, char val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Character.TYPE); jaroslav@601: return (off >= 0) ? Bits.getChar(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public short get(String name, short val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Short.TYPE); jaroslav@601: return (off >= 0) ? Bits.getShort(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public int get(String name, int val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Integer.TYPE); jaroslav@601: return (off >= 0) ? Bits.getInt(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public float get(String name, float val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Float.TYPE); jaroslav@601: return (off >= 0) ? Bits.getFloat(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public long get(String name, long val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Long.TYPE); jaroslav@601: return (off >= 0) ? Bits.getLong(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public double get(String name, double val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Double.TYPE); jaroslav@601: return (off >= 0) ? Bits.getDouble(primVals, off) : val; jaroslav@601: } jaroslav@601: jaroslav@601: public Object get(String name, Object val) throws IOException { jaroslav@601: int off = getFieldOffset(name, Object.class); jaroslav@601: if (off >= 0) { jaroslav@601: int objHandle = objHandles[off]; jaroslav@601: handles.markDependency(passHandle, objHandle); jaroslav@601: return (handles.lookupException(objHandle) == null) ? jaroslav@601: objVals[off] : null; jaroslav@601: } else { jaroslav@601: return val; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads primitive and object field values from stream. jaroslav@601: */ jaroslav@601: void readFields() throws IOException { jaroslav@601: bin.readFully(primVals, 0, primVals.length, false); jaroslav@601: jaroslav@601: int oldHandle = passHandle; jaroslav@601: ObjectStreamField[] fields = desc.getFields(false); jaroslav@601: int numPrimFields = fields.length - objVals.length; jaroslav@601: for (int i = 0; i < objVals.length; i++) { jaroslav@601: objVals[i] = jaroslav@601: readObject0(fields[numPrimFields + i].isUnshared()); jaroslav@601: objHandles[i] = passHandle; jaroslav@601: } jaroslav@601: passHandle = oldHandle; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns offset of field with given name and type. A specified type jaroslav@601: * of null matches all types, Object.class matches all non-primitive jaroslav@601: * types, and any other non-null type matches assignable types only. jaroslav@601: * If no matching field is found in the (incoming) class jaroslav@601: * descriptor but a matching field is present in the associated local jaroslav@601: * class descriptor, returns -1. Throws IllegalArgumentException if jaroslav@601: * neither incoming nor local class descriptor contains a match. jaroslav@601: */ jaroslav@601: private int getFieldOffset(String name, Class type) { jaroslav@601: ObjectStreamField field = desc.getField(name, type); jaroslav@601: if (field != null) { jaroslav@601: return field.getOffset(); jaroslav@601: } else if (desc.getLocalDesc().getField(name, type) != null) { jaroslav@601: return -1; jaroslav@601: } else { jaroslav@601: throw new IllegalArgumentException("no such field " + name + jaroslav@601: " with type " + type); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Prioritized list of callbacks to be performed once object graph has been jaroslav@601: * completely deserialized. jaroslav@601: */ jaroslav@601: private static class ValidationList { jaroslav@601: jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates new (empty) ValidationList. jaroslav@601: */ jaroslav@601: ValidationList() { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Registers callback. Throws InvalidObjectException if callback jaroslav@601: * object is null. jaroslav@601: */ jaroslav@601: void register(ObjectInputValidation obj, int priority) jaroslav@601: throws InvalidObjectException jaroslav@601: { jaroslav@601: if (obj == null) { jaroslav@601: throw new InvalidObjectException("null callback"); jaroslav@601: } jaroslav@604: throw new InvalidObjectException("Does not work."); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes all registered callbacks and clears the callback list. jaroslav@601: * Callbacks with higher priorities are called first; those with equal jaroslav@601: * priorities may be called in any order. If any of the callbacks jaroslav@601: * throws an InvalidObjectException, the callback process is terminated jaroslav@601: * and the exception propagated upwards. jaroslav@601: */ jaroslav@601: void doCallbacks() throws InvalidObjectException { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Resets the callback list to its initial (empty) state. jaroslav@601: */ jaroslav@601: public void clear() { jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Input stream supporting single-byte peek operations. jaroslav@601: */ jaroslav@601: private static class PeekInputStream extends InputStream { jaroslav@601: jaroslav@601: /** underlying stream */ jaroslav@601: private final InputStream in; jaroslav@601: /** peeked byte */ jaroslav@601: private int peekb = -1; jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates new PeekInputStream on top of given underlying stream. jaroslav@601: */ jaroslav@601: PeekInputStream(InputStream in) { jaroslav@601: this.in = in; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Peeks at next byte value in stream. Similar to read(), except jaroslav@601: * that it does not consume the read value. jaroslav@601: */ jaroslav@601: int peek() throws IOException { jaroslav@601: return (peekb >= 0) ? peekb : (peekb = in.read()); jaroslav@601: } jaroslav@601: jaroslav@601: public int read() throws IOException { jaroslav@601: if (peekb >= 0) { jaroslav@601: int v = peekb; jaroslav@601: peekb = -1; jaroslav@601: return v; jaroslav@601: } else { jaroslav@601: return in.read(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: public int read(byte[] b, int off, int len) throws IOException { jaroslav@601: if (len == 0) { jaroslav@601: return 0; jaroslav@601: } else if (peekb < 0) { jaroslav@601: return in.read(b, off, len); jaroslav@601: } else { jaroslav@601: b[off++] = (byte) peekb; jaroslav@601: len--; jaroslav@601: peekb = -1; jaroslav@601: int n = in.read(b, off, len); jaroslav@601: return (n >= 0) ? (n + 1) : 1; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readFully(byte[] b, int off, int len) throws IOException { jaroslav@601: int n = 0; jaroslav@601: while (n < len) { jaroslav@601: int count = read(b, off + n, len - n); jaroslav@601: if (count < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: n += count; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: public long skip(long n) throws IOException { jaroslav@601: if (n <= 0) { jaroslav@601: return 0; jaroslav@601: } jaroslav@601: int skipped = 0; jaroslav@601: if (peekb >= 0) { jaroslav@601: peekb = -1; jaroslav@601: skipped++; jaroslav@601: n--; jaroslav@601: } jaroslav@601: return skipped + skip(n); jaroslav@601: } jaroslav@601: jaroslav@601: public int available() throws IOException { jaroslav@601: return in.available() + ((peekb >= 0) ? 1 : 0); jaroslav@601: } jaroslav@601: jaroslav@601: public void close() throws IOException { jaroslav@601: in.close(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Input stream with two modes: in default mode, inputs data written in the jaroslav@601: * same format as DataOutputStream; in "block data" mode, inputs data jaroslav@601: * bracketed by block data markers (see object serialization specification jaroslav@601: * for details). Buffering depends on block data mode: when in default jaroslav@601: * mode, no data is buffered in advance; when in block data mode, all data jaroslav@601: * for the current data block is read in at once (and buffered). jaroslav@601: */ jaroslav@601: private class BlockDataInputStream jaroslav@601: extends InputStream implements DataInput jaroslav@601: { jaroslav@601: /** maximum data block length */ jaroslav@601: private static final int MAX_BLOCK_SIZE = 1024; jaroslav@601: /** maximum data block header length */ jaroslav@601: private static final int MAX_HEADER_SIZE = 5; jaroslav@601: /** (tunable) length of char buffer (for reading strings) */ jaroslav@601: private static final int CHAR_BUF_SIZE = 256; jaroslav@601: /** readBlockHeader() return value indicating header read may block */ jaroslav@601: private static final int HEADER_BLOCKED = -2; jaroslav@601: jaroslav@601: /** buffer for reading general/block data */ jaroslav@601: private final byte[] buf = new byte[MAX_BLOCK_SIZE]; jaroslav@601: /** buffer for reading block data headers */ jaroslav@601: private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; jaroslav@601: /** char buffer for fast string reads */ jaroslav@601: private final char[] cbuf = new char[CHAR_BUF_SIZE]; jaroslav@601: jaroslav@601: /** block data mode */ jaroslav@601: private boolean blkmode = false; jaroslav@601: jaroslav@601: // block data state fields; values meaningful only when blkmode true jaroslav@601: /** current offset into buf */ jaroslav@601: private int pos = 0; jaroslav@601: /** end offset of valid data in buf, or -1 if no more block data */ jaroslav@601: private int end = -1; jaroslav@601: /** number of bytes in current block yet to be read from stream */ jaroslav@601: private int unread = 0; jaroslav@601: jaroslav@601: /** underlying stream (wrapped in peekable filter stream) */ jaroslav@601: private final PeekInputStream in; jaroslav@601: /** loopback stream (for data reads that span data blocks) */ jaroslav@601: private final DataInputStream din; jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates new BlockDataInputStream on top of given underlying stream. jaroslav@601: * Block data mode is turned off by default. jaroslav@601: */ jaroslav@601: BlockDataInputStream(InputStream in) { jaroslav@601: this.in = new PeekInputStream(in); jaroslav@601: din = new DataInputStream(this); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Sets block data mode to the given mode (true == on, false == off) jaroslav@601: * and returns the previous mode value. If the new mode is the same as jaroslav@601: * the old mode, no action is taken. Throws IllegalStateException if jaroslav@601: * block data mode is being switched from on to off while unconsumed jaroslav@601: * block data is still present in the stream. jaroslav@601: */ jaroslav@601: boolean setBlockDataMode(boolean newmode) throws IOException { jaroslav@601: if (blkmode == newmode) { jaroslav@601: return blkmode; jaroslav@601: } jaroslav@601: if (newmode) { jaroslav@601: pos = 0; jaroslav@601: end = 0; jaroslav@601: unread = 0; jaroslav@601: } else if (pos < end) { jaroslav@601: throw new IllegalStateException("unread block data"); jaroslav@601: } jaroslav@601: blkmode = newmode; jaroslav@601: return !blkmode; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if the stream is currently in block data mode, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: boolean getBlockDataMode() { jaroslav@601: return blkmode; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If in block data mode, skips to the end of the current group of data jaroslav@601: * blocks (but does not unset block data mode). If not in block data jaroslav@601: * mode, throws an IllegalStateException. jaroslav@601: */ jaroslav@601: void skipBlockData() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: throw new IllegalStateException("not in block data mode"); jaroslav@601: } jaroslav@601: while (end >= 0) { jaroslav@601: refill(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Attempts to read in the next block data header (if any). If jaroslav@601: * canBlock is false and a full header cannot be read without possibly jaroslav@601: * blocking, returns HEADER_BLOCKED, else if the next element in the jaroslav@601: * stream is a block data header, returns the block data length jaroslav@601: * specified by the header, else returns -1. jaroslav@601: */ jaroslav@601: private int readBlockHeader(boolean canBlock) throws IOException { jaroslav@601: if (defaultDataEnd) { jaroslav@601: /* jaroslav@601: * Fix for 4360508: stream is currently at the end of a field jaroslav@601: * value block written via default serialization; since there jaroslav@601: * is no terminating TC_ENDBLOCKDATA tag, simulate jaroslav@601: * end-of-custom-data behavior explicitly. jaroslav@601: */ jaroslav@601: return -1; jaroslav@601: } jaroslav@601: try { jaroslav@601: for (;;) { jaroslav@601: int avail = canBlock ? Integer.MAX_VALUE : in.available(); jaroslav@601: if (avail == 0) { jaroslav@601: return HEADER_BLOCKED; jaroslav@601: } jaroslav@601: jaroslav@601: int tc = in.peek(); jaroslav@601: switch (tc) { jaroslav@601: case TC_BLOCKDATA: jaroslav@601: if (avail < 2) { jaroslav@601: return HEADER_BLOCKED; jaroslav@601: } jaroslav@601: in.readFully(hbuf, 0, 2); jaroslav@601: return hbuf[1] & 0xFF; jaroslav@601: jaroslav@601: case TC_BLOCKDATALONG: jaroslav@601: if (avail < 5) { jaroslav@601: return HEADER_BLOCKED; jaroslav@601: } jaroslav@601: in.readFully(hbuf, 0, 5); jaroslav@601: int len = Bits.getInt(hbuf, 1); jaroslav@601: if (len < 0) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "illegal block data header length: " + jaroslav@601: len); jaroslav@601: } jaroslav@601: return len; jaroslav@601: jaroslav@601: /* jaroslav@601: * TC_RESETs may occur in between data blocks. jaroslav@601: * Unfortunately, this case must be parsed at a lower jaroslav@601: * level than other typecodes, since primitive data jaroslav@601: * reads may span data blocks separated by a TC_RESET. jaroslav@601: */ jaroslav@601: case TC_RESET: jaroslav@601: in.read(); jaroslav@601: handleReset(); jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: String.format("invalid type code: %02X", jaroslav@601: tc)); jaroslav@601: } jaroslav@601: return -1; jaroslav@601: } jaroslav@601: } jaroslav@601: } catch (EOFException ex) { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "unexpected EOF while reading block data header"); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Refills internal buffer buf with block data. Any data in buf at the jaroslav@601: * time of the call is considered consumed. Sets the pos, end, and jaroslav@601: * unread fields to reflect the new amount of available block data; if jaroslav@601: * the next element in the stream is not a data block, sets pos and jaroslav@601: * unread to 0 and end to -1. jaroslav@601: */ jaroslav@601: private void refill() throws IOException { jaroslav@601: try { jaroslav@601: do { jaroslav@601: pos = 0; jaroslav@601: if (unread > 0) { jaroslav@601: int n = jaroslav@601: in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE)); jaroslav@601: if (n >= 0) { jaroslav@601: end = n; jaroslav@601: unread -= n; jaroslav@601: } else { jaroslav@601: throw new StreamCorruptedException( jaroslav@601: "unexpected EOF in middle of data block"); jaroslav@601: } jaroslav@601: } else { jaroslav@601: int n = readBlockHeader(true); jaroslav@601: if (n >= 0) { jaroslav@601: end = 0; jaroslav@601: unread = n; jaroslav@601: } else { jaroslav@601: end = -1; jaroslav@601: unread = 0; jaroslav@601: } jaroslav@601: } jaroslav@601: } while (pos == end); jaroslav@601: } catch (IOException ex) { jaroslav@601: pos = 0; jaroslav@601: end = -1; jaroslav@601: unread = 0; jaroslav@601: throw ex; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If in block data mode, returns the number of unconsumed bytes jaroslav@601: * remaining in the current data block. If not in block data mode, jaroslav@601: * throws an IllegalStateException. jaroslav@601: */ jaroslav@601: int currentBlockRemaining() { jaroslav@601: if (blkmode) { jaroslav@601: return (end >= 0) ? (end - pos) + unread : 0; jaroslav@601: } else { jaroslav@601: throw new IllegalStateException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Peeks at (but does not consume) and returns the next byte value in jaroslav@601: * the stream, or -1 if the end of the stream/block data (if in block jaroslav@601: * data mode) has been reached. jaroslav@601: */ jaroslav@601: int peek() throws IOException { jaroslav@601: if (blkmode) { jaroslav@601: if (pos == end) { jaroslav@601: refill(); jaroslav@601: } jaroslav@601: return (end >= 0) ? (buf[pos] & 0xFF) : -1; jaroslav@601: } else { jaroslav@601: return in.peek(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Peeks at (but does not consume) and returns the next byte value in jaroslav@601: * the stream, or throws EOFException if end of stream/block data has jaroslav@601: * been reached. jaroslav@601: */ jaroslav@601: byte peekByte() throws IOException { jaroslav@601: int val = peek(); jaroslav@601: if (val < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: return (byte) val; jaroslav@601: } jaroslav@601: jaroslav@601: jaroslav@601: /* ----------------- generic input stream methods ------------------ */ jaroslav@601: /* jaroslav@601: * The following methods are equivalent to their counterparts in jaroslav@601: * InputStream, except that they interpret data block boundaries and jaroslav@601: * read the requested data from within data blocks when in block data jaroslav@601: * mode. jaroslav@601: */ jaroslav@601: jaroslav@601: public int read() throws IOException { jaroslav@601: if (blkmode) { jaroslav@601: if (pos == end) { jaroslav@601: refill(); jaroslav@601: } jaroslav@601: return (end >= 0) ? (buf[pos++] & 0xFF) : -1; jaroslav@601: } else { jaroslav@601: return in.read(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: public int read(byte[] b, int off, int len) throws IOException { jaroslav@601: return read(b, off, len, false); jaroslav@601: } jaroslav@601: jaroslav@601: public long skip(long len) throws IOException { jaroslav@601: long remain = len; jaroslav@601: while (remain > 0) { jaroslav@601: if (blkmode) { jaroslav@601: if (pos == end) { jaroslav@601: refill(); jaroslav@601: } jaroslav@601: if (end < 0) { jaroslav@601: break; jaroslav@601: } jaroslav@601: int nread = (int) Math.min(remain, end - pos); jaroslav@601: remain -= nread; jaroslav@601: pos += nread; jaroslav@601: } else { jaroslav@601: int nread = (int) Math.min(remain, MAX_BLOCK_SIZE); jaroslav@601: if ((nread = in.read(buf, 0, nread)) < 0) { jaroslav@601: break; jaroslav@601: } jaroslav@601: remain -= nread; jaroslav@601: } jaroslav@601: } jaroslav@601: return len - remain; jaroslav@601: } jaroslav@601: jaroslav@601: public int available() throws IOException { jaroslav@601: if (blkmode) { jaroslav@601: if ((pos == end) && (unread == 0)) { jaroslav@601: int n; jaroslav@601: while ((n = readBlockHeader(false)) == 0) ; jaroslav@601: switch (n) { jaroslav@601: case HEADER_BLOCKED: jaroslav@601: break; jaroslav@601: jaroslav@601: case -1: jaroslav@601: pos = 0; jaroslav@601: end = -1; jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: pos = 0; jaroslav@601: end = 0; jaroslav@601: unread = n; jaroslav@601: break; jaroslav@601: } jaroslav@601: } jaroslav@601: // avoid unnecessary call to in.available() if possible jaroslav@601: int unreadAvail = (unread > 0) ? jaroslav@601: Math.min(in.available(), unread) : 0; jaroslav@601: return (end >= 0) ? (end - pos) + unreadAvail : 0; jaroslav@601: } else { jaroslav@601: return in.available(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: public void close() throws IOException { jaroslav@601: if (blkmode) { jaroslav@601: pos = 0; jaroslav@601: end = -1; jaroslav@601: unread = 0; jaroslav@601: } jaroslav@601: in.close(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Attempts to read len bytes into byte array b at offset off. Returns jaroslav@601: * the number of bytes read, or -1 if the end of stream/block data has jaroslav@601: * been reached. If copy is true, reads values into an intermediate jaroslav@601: * buffer before copying them to b (to avoid exposing a reference to jaroslav@601: * b). jaroslav@601: */ jaroslav@601: int read(byte[] b, int off, int len, boolean copy) throws IOException { jaroslav@601: if (len == 0) { jaroslav@601: return 0; jaroslav@601: } else if (blkmode) { jaroslav@601: if (pos == end) { jaroslav@601: refill(); jaroslav@601: } jaroslav@601: if (end < 0) { jaroslav@601: return -1; jaroslav@601: } jaroslav@601: int nread = Math.min(len, end - pos); jaroslav@601: System.arraycopy(buf, pos, b, off, nread); jaroslav@601: pos += nread; jaroslav@601: return nread; jaroslav@601: } else if (copy) { jaroslav@601: int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE)); jaroslav@601: if (nread > 0) { jaroslav@601: System.arraycopy(buf, 0, b, off, nread); jaroslav@601: } jaroslav@601: return nread; jaroslav@601: } else { jaroslav@601: return in.read(b, off, len); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /* ----------------- primitive data input methods ------------------ */ jaroslav@601: /* jaroslav@601: * The following methods are equivalent to their counterparts in jaroslav@601: * DataInputStream, except that they interpret data block boundaries jaroslav@601: * and read the requested data from within data blocks when in block jaroslav@601: * data mode. jaroslav@601: */ jaroslav@601: jaroslav@601: public void readFully(byte[] b) throws IOException { jaroslav@601: readFully(b, 0, b.length, false); jaroslav@601: } jaroslav@601: jaroslav@601: public void readFully(byte[] b, int off, int len) throws IOException { jaroslav@601: readFully(b, off, len, false); jaroslav@601: } jaroslav@601: jaroslav@601: public void readFully(byte[] b, int off, int len, boolean copy) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: while (len > 0) { jaroslav@601: int n = read(b, off, len, copy); jaroslav@601: if (n < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: off += n; jaroslav@601: len -= n; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: public int skipBytes(int n) throws IOException { jaroslav@601: return din.skipBytes(n); jaroslav@601: } jaroslav@601: jaroslav@601: public boolean readBoolean() throws IOException { jaroslav@601: int v = read(); jaroslav@601: if (v < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: return (v != 0); jaroslav@601: } jaroslav@601: jaroslav@601: public byte readByte() throws IOException { jaroslav@601: int v = read(); jaroslav@601: if (v < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: return (byte) v; jaroslav@601: } jaroslav@601: jaroslav@601: public int readUnsignedByte() throws IOException { jaroslav@601: int v = read(); jaroslav@601: if (v < 0) { jaroslav@601: throw new EOFException(); jaroslav@601: } jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public char readChar() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 2); jaroslav@601: } else if (end - pos < 2) { jaroslav@601: return din.readChar(); jaroslav@601: } jaroslav@601: char v = Bits.getChar(buf, pos); jaroslav@601: pos += 2; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public short readShort() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 2); jaroslav@601: } else if (end - pos < 2) { jaroslav@601: return din.readShort(); jaroslav@601: } jaroslav@601: short v = Bits.getShort(buf, pos); jaroslav@601: pos += 2; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public int readUnsignedShort() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 2); jaroslav@601: } else if (end - pos < 2) { jaroslav@601: return din.readUnsignedShort(); jaroslav@601: } jaroslav@601: int v = Bits.getShort(buf, pos) & 0xFFFF; jaroslav@601: pos += 2; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public int readInt() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 4); jaroslav@601: } else if (end - pos < 4) { jaroslav@601: return din.readInt(); jaroslav@601: } jaroslav@601: int v = Bits.getInt(buf, pos); jaroslav@601: pos += 4; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public float readFloat() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 4); jaroslav@601: } else if (end - pos < 4) { jaroslav@601: return din.readFloat(); jaroslav@601: } jaroslav@601: float v = Bits.getFloat(buf, pos); jaroslav@601: pos += 4; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public long readLong() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 8); jaroslav@601: } else if (end - pos < 8) { jaroslav@601: return din.readLong(); jaroslav@601: } jaroslav@601: long v = Bits.getLong(buf, pos); jaroslav@601: pos += 8; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public double readDouble() throws IOException { jaroslav@601: if (!blkmode) { jaroslav@601: pos = 0; jaroslav@601: in.readFully(buf, 0, 8); jaroslav@601: } else if (end - pos < 8) { jaroslav@601: return din.readDouble(); jaroslav@601: } jaroslav@601: double v = Bits.getDouble(buf, pos); jaroslav@601: pos += 8; jaroslav@601: return v; jaroslav@601: } jaroslav@601: jaroslav@601: public String readUTF() throws IOException { jaroslav@601: return readUTFBody(readUnsignedShort()); jaroslav@601: } jaroslav@601: jaroslav@601: public String readLine() throws IOException { jaroslav@601: return din.readLine(); // deprecated, not worth optimizing jaroslav@601: } jaroslav@601: jaroslav@601: /* -------------- primitive data array input methods --------------- */ jaroslav@601: /* jaroslav@601: * The following methods read in spans of primitive data values. jaroslav@601: * Though equivalent to calling the corresponding primitive read jaroslav@601: * methods repeatedly, these methods are optimized for reading groups jaroslav@601: * of primitive data values more efficiently. jaroslav@601: */ jaroslav@601: jaroslav@601: void readBooleans(boolean[] v, int off, int len) throws IOException { jaroslav@601: int stop, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: int span = Math.min(endoff - off, MAX_BLOCK_SIZE); jaroslav@601: in.readFully(buf, 0, span); jaroslav@601: stop = off + span; jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 1) { jaroslav@601: v[off++] = din.readBoolean(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: stop = Math.min(endoff, off + end - pos); jaroslav@601: } jaroslav@601: jaroslav@601: while (off < stop) { jaroslav@601: v[off++] = Bits.getBoolean(buf, pos++); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readChars(char[] v, int off, int len) throws IOException { jaroslav@601: int stop, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); jaroslav@601: in.readFully(buf, 0, span << 1); jaroslav@601: stop = off + span; jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 2) { jaroslav@601: v[off++] = din.readChar(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: stop = Math.min(endoff, off + ((end - pos) >> 1)); jaroslav@601: } jaroslav@601: jaroslav@601: while (off < stop) { jaroslav@601: v[off++] = Bits.getChar(buf, pos); jaroslav@601: pos += 2; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readShorts(short[] v, int off, int len) throws IOException { jaroslav@601: int stop, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); jaroslav@601: in.readFully(buf, 0, span << 1); jaroslav@601: stop = off + span; jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 2) { jaroslav@601: v[off++] = din.readShort(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: stop = Math.min(endoff, off + ((end - pos) >> 1)); jaroslav@601: } jaroslav@601: jaroslav@601: while (off < stop) { jaroslav@601: v[off++] = Bits.getShort(buf, pos); jaroslav@601: pos += 2; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readInts(int[] v, int off, int len) throws IOException { jaroslav@601: int stop, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); jaroslav@601: in.readFully(buf, 0, span << 2); jaroslav@601: stop = off + span; jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 4) { jaroslav@601: v[off++] = din.readInt(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: stop = Math.min(endoff, off + ((end - pos) >> 2)); jaroslav@601: } jaroslav@601: jaroslav@601: while (off < stop) { jaroslav@601: v[off++] = Bits.getInt(buf, pos); jaroslav@601: pos += 4; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readFloats(float[] v, int off, int len) throws IOException { jaroslav@601: int span, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); jaroslav@601: in.readFully(buf, 0, span << 2); jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 4) { jaroslav@601: v[off++] = din.readFloat(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: span = Math.min(endoff - off, ((end - pos) >> 2)); jaroslav@601: } jaroslav@601: jaroslav@601: bytesToFloats(buf, pos, v, off, span); jaroslav@601: off += span; jaroslav@601: pos += span << 2; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readLongs(long[] v, int off, int len) throws IOException { jaroslav@601: int stop, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); jaroslav@601: in.readFully(buf, 0, span << 3); jaroslav@601: stop = off + span; jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 8) { jaroslav@601: v[off++] = din.readLong(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: stop = Math.min(endoff, off + ((end - pos) >> 3)); jaroslav@601: } jaroslav@601: jaroslav@601: while (off < stop) { jaroslav@601: v[off++] = Bits.getLong(buf, pos); jaroslav@601: pos += 8; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: void readDoubles(double[] v, int off, int len) throws IOException { jaroslav@601: int span, endoff = off + len; jaroslav@601: while (off < endoff) { jaroslav@601: if (!blkmode) { jaroslav@601: span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); jaroslav@601: in.readFully(buf, 0, span << 3); jaroslav@601: pos = 0; jaroslav@601: } else if (end - pos < 8) { jaroslav@601: v[off++] = din.readDouble(); jaroslav@601: continue; jaroslav@601: } else { jaroslav@601: span = Math.min(endoff - off, ((end - pos) >> 3)); jaroslav@601: } jaroslav@601: jaroslav@601: bytesToDoubles(buf, pos, v, off, span); jaroslav@601: off += span; jaroslav@601: pos += span << 3; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in string written in "long" UTF format. "Long" UTF format is jaroslav@601: * identical to standard UTF, except that it uses an 8 byte header jaroslav@601: * (instead of the standard 2 bytes) to convey the UTF encoding length. jaroslav@601: */ jaroslav@601: String readLongUTF() throws IOException { jaroslav@601: return readUTFBody(readLong()); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in the "body" (i.e., the UTF representation minus the 2-byte jaroslav@601: * or 8-byte length header) of a UTF encoding, which occupies the next jaroslav@601: * utflen bytes. jaroslav@601: */ jaroslav@601: private String readUTFBody(long utflen) throws IOException { jaroslav@601: StringBuilder sbuf = new StringBuilder(); jaroslav@601: if (!blkmode) { jaroslav@601: end = pos = 0; jaroslav@601: } jaroslav@601: jaroslav@601: while (utflen > 0) { jaroslav@601: int avail = end - pos; jaroslav@601: if (avail >= 3 || (long) avail == utflen) { jaroslav@601: utflen -= readUTFSpan(sbuf, utflen); jaroslav@601: } else { jaroslav@601: if (blkmode) { jaroslav@601: // near block boundary, read one byte at a time jaroslav@601: utflen -= readUTFChar(sbuf, utflen); jaroslav@601: } else { jaroslav@601: // shift and refill buffer manually jaroslav@601: if (avail > 0) { jaroslav@601: System.arraycopy(buf, pos, buf, 0, avail); jaroslav@601: } jaroslav@601: pos = 0; jaroslav@601: end = (int) Math.min(MAX_BLOCK_SIZE, utflen); jaroslav@601: in.readFully(buf, avail, end - avail); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: return sbuf.toString(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads span of UTF-encoded characters out of internal buffer jaroslav@601: * (starting at offset pos and ending at or before offset end), jaroslav@601: * consuming no more than utflen bytes. Appends read characters to jaroslav@601: * sbuf. Returns the number of bytes consumed. jaroslav@601: */ jaroslav@601: private long readUTFSpan(StringBuilder sbuf, long utflen) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: int cpos = 0; jaroslav@601: int start = pos; jaroslav@601: int avail = Math.min(end - pos, CHAR_BUF_SIZE); jaroslav@601: // stop short of last char unless all of utf bytes in buffer jaroslav@601: int stop = pos + ((utflen > avail) ? avail - 2 : (int) utflen); jaroslav@601: boolean outOfBounds = false; jaroslav@601: jaroslav@601: try { jaroslav@601: while (pos < stop) { jaroslav@601: int b1, b2, b3; jaroslav@601: b1 = buf[pos++] & 0xFF; jaroslav@601: switch (b1 >> 4) { jaroslav@601: case 0: jaroslav@601: case 1: jaroslav@601: case 2: jaroslav@601: case 3: jaroslav@601: case 4: jaroslav@601: case 5: jaroslav@601: case 6: jaroslav@601: case 7: // 1 byte format: 0xxxxxxx jaroslav@601: cbuf[cpos++] = (char) b1; jaroslav@601: break; jaroslav@601: jaroslav@601: case 12: jaroslav@601: case 13: // 2 byte format: 110xxxxx 10xxxxxx jaroslav@601: b2 = buf[pos++]; jaroslav@601: if ((b2 & 0xC0) != 0x80) { jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) | jaroslav@601: ((b2 & 0x3F) << 0)); jaroslav@601: break; jaroslav@601: jaroslav@601: case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx jaroslav@601: b3 = buf[pos + 1]; jaroslav@601: b2 = buf[pos + 0]; jaroslav@601: pos += 2; jaroslav@601: if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) | jaroslav@601: ((b2 & 0x3F) << 6) | jaroslav@601: ((b3 & 0x3F) << 0)); jaroslav@601: break; jaroslav@601: jaroslav@601: default: // 10xx xxxx, 1111 xxxx jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: } jaroslav@601: } catch (ArrayIndexOutOfBoundsException ex) { jaroslav@601: outOfBounds = true; jaroslav@601: } finally { jaroslav@601: if (outOfBounds || (pos - start) > utflen) { jaroslav@601: /* jaroslav@601: * Fix for 4450867: if a malformed utf char causes the jaroslav@601: * conversion loop to scan past the expected end of the utf jaroslav@601: * string, only consume the expected number of utf bytes. jaroslav@601: */ jaroslav@601: pos = start + (int) utflen; jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: sbuf.append(cbuf, 0, cpos); jaroslav@601: return pos - start; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads in single UTF-encoded character one byte at a time, appends jaroslav@601: * the character to sbuf, and returns the number of bytes consumed. jaroslav@601: * This method is used when reading in UTF strings written in block jaroslav@601: * data mode to handle UTF-encoded characters which (potentially) jaroslav@601: * straddle block-data boundaries. jaroslav@601: */ jaroslav@601: private int readUTFChar(StringBuilder sbuf, long utflen) jaroslav@601: throws IOException jaroslav@601: { jaroslav@601: int b1, b2, b3; jaroslav@601: b1 = readByte() & 0xFF; jaroslav@601: switch (b1 >> 4) { jaroslav@601: case 0: jaroslav@601: case 1: jaroslav@601: case 2: jaroslav@601: case 3: jaroslav@601: case 4: jaroslav@601: case 5: jaroslav@601: case 6: jaroslav@601: case 7: // 1 byte format: 0xxxxxxx jaroslav@601: sbuf.append((char) b1); jaroslav@601: return 1; jaroslav@601: jaroslav@601: case 12: jaroslav@601: case 13: // 2 byte format: 110xxxxx 10xxxxxx jaroslav@601: if (utflen < 2) { jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: b2 = readByte(); jaroslav@601: if ((b2 & 0xC0) != 0x80) { jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: sbuf.append((char) (((b1 & 0x1F) << 6) | jaroslav@601: ((b2 & 0x3F) << 0))); jaroslav@601: return 2; jaroslav@601: jaroslav@601: case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx jaroslav@601: if (utflen < 3) { jaroslav@601: if (utflen == 2) { jaroslav@601: readByte(); // consume remaining byte jaroslav@601: } jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: b2 = readByte(); jaroslav@601: b3 = readByte(); jaroslav@601: if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: sbuf.append((char) (((b1 & 0x0F) << 12) | jaroslav@601: ((b2 & 0x3F) << 6) | jaroslav@601: ((b3 & 0x3F) << 0))); jaroslav@601: return 3; jaroslav@601: jaroslav@601: default: // 10xx xxxx, 1111 xxxx jaroslav@601: throw new UTFDataFormatException(); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Unsynchronized table which tracks wire handle to object mappings, as jaroslav@601: * well as ClassNotFoundExceptions associated with deserialized objects. jaroslav@601: * This class implements an exception-propagation algorithm for jaroslav@601: * determining which objects should have ClassNotFoundExceptions associated jaroslav@601: * with them, taking into account cycles and discontinuities (e.g., skipped jaroslav@601: * fields) in the object graph. jaroslav@601: * jaroslav@601: *

General use of the table is as follows: during deserialization, a jaroslav@601: * given object is first assigned a handle by calling the assign method. jaroslav@601: * This method leaves the assigned handle in an "open" state, wherein jaroslav@601: * dependencies on the exception status of other handles can be registered jaroslav@601: * by calling the markDependency method, or an exception can be directly jaroslav@601: * associated with the handle by calling markException. When a handle is jaroslav@601: * tagged with an exception, the HandleTable assumes responsibility for jaroslav@601: * propagating the exception to any other objects which depend jaroslav@601: * (transitively) on the exception-tagged object. jaroslav@601: * jaroslav@601: *

Once all exception information/dependencies for the handle have been jaroslav@601: * registered, the handle should be "closed" by calling the finish method jaroslav@601: * on it. The act of finishing a handle allows the exception propagation jaroslav@601: * algorithm to aggressively prune dependency links, lessening the jaroslav@601: * performance/memory impact of exception tracking. jaroslav@601: * jaroslav@601: *

Note that the exception propagation algorithm used depends on handles jaroslav@601: * being assigned/finished in LIFO order; however, for simplicity as well jaroslav@601: * as memory conservation, it does not enforce this constraint. jaroslav@601: */ jaroslav@601: // REMIND: add full description of exception propagation algorithm? jaroslav@601: private static class HandleTable { jaroslav@601: jaroslav@601: /* status codes indicating whether object has associated exception */ jaroslav@601: private static final byte STATUS_OK = 1; jaroslav@601: private static final byte STATUS_UNKNOWN = 2; jaroslav@601: private static final byte STATUS_EXCEPTION = 3; jaroslav@601: jaroslav@601: /** array mapping handle -> object status */ jaroslav@601: byte[] status; jaroslav@601: /** array mapping handle -> object/exception (depending on status) */ jaroslav@601: Object[] entries; jaroslav@601: /** array mapping handle -> list of dependent handles (if any) */ jaroslav@601: HandleList[] deps; jaroslav@601: /** lowest unresolved dependency */ jaroslav@601: int lowDep = -1; jaroslav@601: /** number of handles in table */ jaroslav@601: int size = 0; jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates handle table with the given initial capacity. jaroslav@601: */ jaroslav@601: HandleTable(int initialCapacity) { jaroslav@601: status = new byte[initialCapacity]; jaroslav@601: entries = new Object[initialCapacity]; jaroslav@601: deps = new HandleList[initialCapacity]; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Assigns next available handle to given object, and returns assigned jaroslav@601: * handle. Once object has been completely deserialized (and all jaroslav@601: * dependencies on other objects identified), the handle should be jaroslav@601: * "closed" by passing it to finish(). jaroslav@601: */ jaroslav@601: int assign(Object obj) { jaroslav@601: if (size >= entries.length) { jaroslav@601: grow(); jaroslav@601: } jaroslav@601: status[size] = STATUS_UNKNOWN; jaroslav@601: entries[size] = obj; jaroslav@601: return size++; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Registers a dependency (in exception status) of one handle on jaroslav@601: * another. The dependent handle must be "open" (i.e., assigned, but jaroslav@601: * not finished yet). No action is taken if either dependent or target jaroslav@601: * handle is NULL_HANDLE. jaroslav@601: */ jaroslav@601: void markDependency(int dependent, int target) { jaroslav@601: if (dependent == NULL_HANDLE || target == NULL_HANDLE) { jaroslav@601: return; jaroslav@601: } jaroslav@601: switch (status[dependent]) { jaroslav@601: jaroslav@601: case STATUS_UNKNOWN: jaroslav@601: switch (status[target]) { jaroslav@601: case STATUS_OK: jaroslav@601: // ignore dependencies on objs with no exception jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_EXCEPTION: jaroslav@601: // eagerly propagate exception jaroslav@601: markException(dependent, jaroslav@601: (ClassNotFoundException) entries[target]); jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_UNKNOWN: jaroslav@601: // add to dependency list of target jaroslav@601: if (deps[target] == null) { jaroslav@601: deps[target] = new HandleList(); jaroslav@601: } jaroslav@601: deps[target].add(dependent); jaroslav@601: jaroslav@601: // remember lowest unresolved target seen jaroslav@601: if (lowDep < 0 || lowDep > target) { jaroslav@601: lowDep = target; jaroslav@601: } jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_EXCEPTION: jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Associates a ClassNotFoundException (if one not already associated) jaroslav@601: * with the currently active handle and propagates it to other jaroslav@601: * referencing objects as appropriate. The specified handle must be jaroslav@601: * "open" (i.e., assigned, but not finished yet). jaroslav@601: */ jaroslav@601: void markException(int handle, ClassNotFoundException ex) { jaroslav@601: switch (status[handle]) { jaroslav@601: case STATUS_UNKNOWN: jaroslav@601: status[handle] = STATUS_EXCEPTION; jaroslav@601: entries[handle] = ex; jaroslav@601: jaroslav@601: // propagate exception to dependents jaroslav@601: HandleList dlist = deps[handle]; jaroslav@601: if (dlist != null) { jaroslav@601: int ndeps = dlist.size(); jaroslav@601: for (int i = 0; i < ndeps; i++) { jaroslav@601: markException(dlist.get(i), ex); jaroslav@601: } jaroslav@601: deps[handle] = null; jaroslav@601: } jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_EXCEPTION: jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Marks given handle as finished, meaning that no new dependencies jaroslav@601: * will be marked for handle. Calls to the assign and finish methods jaroslav@601: * must occur in LIFO order. jaroslav@601: */ jaroslav@601: void finish(int handle) { jaroslav@601: int end; jaroslav@601: if (lowDep < 0) { jaroslav@601: // no pending unknowns, only resolve current handle jaroslav@601: end = handle + 1; jaroslav@601: } else if (lowDep >= handle) { jaroslav@601: // pending unknowns now clearable, resolve all upward handles jaroslav@601: end = size; jaroslav@601: lowDep = -1; jaroslav@601: } else { jaroslav@601: // unresolved backrefs present, can't resolve anything yet jaroslav@601: return; jaroslav@601: } jaroslav@601: jaroslav@601: // change STATUS_UNKNOWN -> STATUS_OK in selected span of handles jaroslav@601: for (int i = handle; i < end; i++) { jaroslav@601: switch (status[i]) { jaroslav@601: case STATUS_UNKNOWN: jaroslav@601: status[i] = STATUS_OK; jaroslav@601: deps[i] = null; jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_OK: jaroslav@601: case STATUS_EXCEPTION: jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Assigns a new object to the given handle. The object previously jaroslav@601: * associated with the handle is forgotten. This method has no effect jaroslav@601: * if the given handle already has an exception associated with it. jaroslav@601: * This method may be called at any time after the handle is assigned. jaroslav@601: */ jaroslav@601: void setObject(int handle, Object obj) { jaroslav@601: switch (status[handle]) { jaroslav@601: case STATUS_UNKNOWN: jaroslav@601: case STATUS_OK: jaroslav@601: entries[handle] = obj; jaroslav@601: break; jaroslav@601: jaroslav@601: case STATUS_EXCEPTION: jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Looks up and returns object associated with the given handle. jaroslav@601: * Returns null if the given handle is NULL_HANDLE, or if it has an jaroslav@601: * associated ClassNotFoundException. jaroslav@601: */ jaroslav@601: Object lookupObject(int handle) { jaroslav@601: return (handle != NULL_HANDLE && jaroslav@601: status[handle] != STATUS_EXCEPTION) ? jaroslav@601: entries[handle] : null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Looks up and returns ClassNotFoundException associated with the jaroslav@601: * given handle. Returns null if the given handle is NULL_HANDLE, or jaroslav@601: * if there is no ClassNotFoundException associated with the handle. jaroslav@601: */ jaroslav@601: ClassNotFoundException lookupException(int handle) { jaroslav@601: return (handle != NULL_HANDLE && jaroslav@601: status[handle] == STATUS_EXCEPTION) ? jaroslav@601: (ClassNotFoundException) entries[handle] : null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Resets table to its initial state. jaroslav@601: */ jaroslav@601: void clear() { jaroslav@601: Arrays.fill(status, 0, size, (byte) 0); jaroslav@601: Arrays.fill(entries, 0, size, null); jaroslav@601: Arrays.fill(deps, 0, size, null); jaroslav@601: lowDep = -1; jaroslav@601: size = 0; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns number of handles registered in table. jaroslav@601: */ jaroslav@601: int size() { jaroslav@601: return size; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Expands capacity of internal arrays. jaroslav@601: */ jaroslav@601: private void grow() { jaroslav@601: int newCapacity = (entries.length << 1) + 1; jaroslav@601: jaroslav@601: byte[] newStatus = new byte[newCapacity]; jaroslav@601: Object[] newEntries = new Object[newCapacity]; jaroslav@601: HandleList[] newDeps = new HandleList[newCapacity]; jaroslav@601: jaroslav@601: System.arraycopy(status, 0, newStatus, 0, size); jaroslav@601: System.arraycopy(entries, 0, newEntries, 0, size); jaroslav@601: System.arraycopy(deps, 0, newDeps, 0, size); jaroslav@601: jaroslav@601: status = newStatus; jaroslav@601: entries = newEntries; jaroslav@601: deps = newDeps; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Simple growable list of (integer) handles. jaroslav@601: */ jaroslav@601: private static class HandleList { jaroslav@601: private int[] list = new int[4]; jaroslav@601: private int size = 0; jaroslav@601: jaroslav@601: public HandleList() { jaroslav@601: } jaroslav@601: jaroslav@601: public void add(int handle) { jaroslav@601: if (size >= list.length) { jaroslav@601: int[] newList = new int[list.length << 1]; jaroslav@601: System.arraycopy(list, 0, newList, 0, list.length); jaroslav@601: list = newList; jaroslav@601: } jaroslav@601: list[size++] = handle; jaroslav@601: } jaroslav@601: jaroslav@601: public int get(int index) { jaroslav@601: if (index >= size) { jaroslav@601: throw new ArrayIndexOutOfBoundsException(); jaroslav@601: } jaroslav@601: return list[index]; jaroslav@601: } jaroslav@601: jaroslav@601: public int size() { jaroslav@601: return size; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Method for cloning arrays in case of using unsharing reading jaroslav@601: */ jaroslav@601: private static Object cloneArray(Object array) { jaroslav@601: if (array instanceof Object[]) { jaroslav@601: return ((Object[]) array).clone(); jaroslav@601: } else if (array instanceof boolean[]) { jaroslav@601: return ((boolean[]) array).clone(); jaroslav@601: } else if (array instanceof byte[]) { jaroslav@601: return ((byte[]) array).clone(); jaroslav@601: } else if (array instanceof char[]) { jaroslav@601: return ((char[]) array).clone(); jaroslav@601: } else if (array instanceof double[]) { jaroslav@601: return ((double[]) array).clone(); jaroslav@601: } else if (array instanceof float[]) { jaroslav@601: return ((float[]) array).clone(); jaroslav@601: } else if (array instanceof int[]) { jaroslav@601: return ((int[]) array).clone(); jaroslav@601: } else if (array instanceof long[]) { jaroslav@601: return ((long[]) array).clone(); jaroslav@601: } else if (array instanceof short[]) { jaroslav@601: return ((short[]) array).clone(); jaroslav@601: } else { jaroslav@601: throw new AssertionError(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: }