diff -r 5652acd48509 -r 42bc1e89134d emul/compact/src/main/java/java/io/ObjectOutputStream.java --- a/emul/compact/src/main/java/java/io/ObjectOutputStream.java Mon Feb 25 19:00:08 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2369 +0,0 @@ -/* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.io; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.apidesign.bck2brwsr.emul.lang.System; - -/** - * An ObjectOutputStream writes primitive data types and graphs of Java objects - * to an OutputStream. The objects can be read (reconstituted) using an - * ObjectInputStream. Persistent storage of objects can be accomplished by - * using a file for the stream. If the stream is a network socket stream, the - * objects can be reconstituted on another host or in another process. - * - *
Only objects that support the java.io.Serializable interface can be - * written to streams. The class of each serializable object is encoded - * including the class name and signature of the class, the values of the - * object's fields and arrays, and the closure of any other objects referenced - * from the initial objects. - * - *
The method writeObject is used to write an object to the stream. Any - * object, including Strings and arrays, is written with writeObject. Multiple - * objects or primitives can be written to the stream. The objects must be - * read back from the corresponding ObjectInputstream with the same types and - * in the same order as they were written. - * - *
Primitive data types can also be written to the stream using the - * appropriate methods from DataOutput. Strings can also be written using the - * writeUTF method. - * - *
The default serialization mechanism for an object writes the class of the - * object, the class signature, and the values of all non-transient and - * non-static fields. References to other objects (except in transient or - * static fields) cause those objects to be written also. Multiple references - * to a single object are encoded using a reference sharing mechanism so that - * graphs of objects can be restored to the same shape as when the original was - * written. - * - *
For example to write an object that can be read by the example in
- * ObjectInputStream:
- *
- *
- * FileOutputStream fos = new FileOutputStream("t.tmp"); - * ObjectOutputStream oos = new ObjectOutputStream(fos); - * - * oos.writeInt(12345); - * oos.writeObject("Today"); - * oos.writeObject(new Date()); - * - * oos.close(); - *- * - *
Classes that require special handling during the serialization and
- * deserialization process must implement special methods with these exact
- * signatures:
- *
- *
- * private void readObject(java.io.ObjectInputStream stream) - * throws IOException, ClassNotFoundException; - * private void writeObject(java.io.ObjectOutputStream stream) - * throws IOException - * private void readObjectNoData() - * throws ObjectStreamException; - *- * - *
The writeObject method is responsible for writing the state of the object - * for its particular class so that the corresponding readObject method can - * restore it. The method does not need to concern itself with the state - * belonging to the object's superclasses or subclasses. State is saved by - * writing the individual fields to the ObjectOutputStream using the - * writeObject method or by using the methods for primitive data types - * supported by DataOutput. - * - *
Serialization does not write out the fields of any object that does not - * implement the java.io.Serializable interface. Subclasses of Objects that - * are not serializable can be serializable. In this case the non-serializable - * class must have a no-arg constructor to allow its fields to be initialized. - * In this case it is the responsibility of the subclass to save and restore - * the state of the non-serializable class. It is frequently the case that the - * fields of that class are accessible (public, package, or protected) or that - * there are get and set methods that can be used to restore the state. - * - *
Serialization of an object can be prevented by implementing writeObject - * and readObject methods that throw the NotSerializableException. The - * exception will be caught by the ObjectOutputStream and abort the - * serialization process. - * - *
Implementing the Externalizable interface allows the object to assume - * complete control over the contents and format of the object's serialized - * form. The methods of the Externalizable interface, writeExternal and - * readExternal, are called to save and restore the objects state. When - * implemented by a class they can write and read their own state using all of - * the methods of ObjectOutput and ObjectInput. It is the responsibility of - * the objects to handle any versioning that occurs. - * - *
Enum constants are serialized differently than ordinary serializable or - * externalizable objects. The serialized form of an enum constant consists - * solely of its name; field values of the constant are not transmitted. To - * serialize an enum constant, ObjectOutputStream writes the string returned by - * the constant's name method. Like other serializable or externalizable - * objects, enum constants can function as the targets of back references - * appearing subsequently in the serialization stream. The process by which - * enum constants are serialized cannot be customized; any class-specific - * writeObject and writeReplace methods defined by enum types are ignored - * during serialization. Similarly, any serialPersistentFields or - * serialVersionUID field declarations are also ignored--all enum types have a - * fixed serialVersionUID of 0L. - * - *
Primitive data, excluding serializable fields and externalizable data, is - * written to the ObjectOutputStream in block-data records. A block data record - * is composed of a header and data. The block data header consists of a marker - * and the number of bytes to follow the header. Consecutive primitive data - * writes are merged into one block-data record. The blocking factor used for - * a block-data record will be 1024 bytes. Each block-data record will be - * filled up to 1024 bytes, or be written whenever there is a termination of - * block-data mode. Calls to the ObjectOutputStream methods writeObject, - * defaultWriteObject and writeFields initially terminate any existing - * block-data record. - * - * @author Mike Warres - * @author Roger Riggs - * @see java.io.DataOutput - * @see java.io.ObjectInputStream - * @see java.io.Serializable - * @see java.io.Externalizable - * @see Object Serialization Specification, Section 2, Object Output Classes - * @since JDK1.1 - */ -public class ObjectOutputStream - extends OutputStream implements ObjectOutput, ObjectStreamConstants -{ - /** filter stream for handling block data conversion */ - private final BlockDataOutputStream bout; - /** obj -> wire handle map */ - private final HandleTable handles; - /** obj -> replacement obj map */ - private final ReplaceTable subs; - /** stream protocol version */ - private int protocol = PROTOCOL_VERSION_2; - /** recursion depth */ - private int depth; - - /** buffer for writing primitive field values */ - private byte[] primVals; - - /** if true, invoke writeObjectOverride() instead of writeObject() */ - private final boolean enableOverride; - /** if true, invoke replaceObject() */ - private boolean enableReplace; - - // values below valid only during upcalls to writeObject()/writeExternal() - /** - * Context during upcalls to class-defined writeObject methods; holds - * object currently being serialized and descriptor for current class. - * Null when not during writeObject upcall. - */ - private Object curContext; - /** current PutField object */ - private PutFieldImpl curPut; - - /** custom storage for debug trace info */ - private final DebugTraceInfoStack debugInfoStack; - - /** - * value of "sun.io.serialization.extendedDebugInfo" property, - * as true or false for extended information about exception's place - */ - private static final boolean extendedDebugInfo = false; - - /** - * Creates an ObjectOutputStream that writes to the specified OutputStream. - * This constructor writes the serialization stream header to the - * underlying stream; callers may wish to flush the stream immediately to - * ensure that constructors for receiving ObjectInputStreams will not block - * when reading the header. - * - *
If a security manager is installed, this constructor will check for
- * the "enableSubclassImplementation" SerializablePermission when invoked
- * directly or indirectly by the constructor of a subclass which overrides
- * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
- * methods.
- *
- * @param out output stream to write to
- * @throws IOException if an I/O error occurs while writing stream header
- * @throws SecurityException if untrusted subclass illegally overrides
- * security-sensitive methods
- * @throws NullPointerException if out
is null
- * @since 1.4
- * @see ObjectOutputStream#ObjectOutputStream()
- * @see ObjectOutputStream#putFields()
- * @see ObjectInputStream#ObjectInputStream(InputStream)
- */
- public ObjectOutputStream(OutputStream out) throws IOException {
- verifySubclass();
- bout = new BlockDataOutputStream(out);
- handles = new HandleTable(10, (float) 3.00);
- subs = new ReplaceTable(10, (float) 3.00);
- enableOverride = false;
- writeStreamHeader();
- bout.setBlockDataMode(true);
- if (extendedDebugInfo) {
- debugInfoStack = new DebugTraceInfoStack();
- } else {
- debugInfoStack = null;
- }
- }
-
- /**
- * Provide a way for subclasses that are completely reimplementing
- * ObjectOutputStream to not have to allocate private data just used by
- * this implementation of ObjectOutputStream.
- *
- *
If there is a security manager installed, this method first calls the
- * security manager's checkPermission
method with a
- * SerializablePermission("enableSubclassImplementation")
- * permission to ensure it's ok to enable subclassing.
- *
- * @throws SecurityException if a security manager exists and its
- * checkPermission
method denies enabling
- * subclassing.
- * @see SecurityManager#checkPermission
- * @see java.io.SerializablePermission
- */
- protected ObjectOutputStream() throws IOException, SecurityException {
- throw new SecurityException();
- }
-
- /**
- * Specify stream protocol version to use when writing the stream.
- *
- *
This routine provides a hook to enable the current version of - * Serialization to write in a format that is backwards compatible to a - * previous version of the stream format. - * - *
Every effort will be made to avoid introducing additional - * backwards incompatibilities; however, sometimes there is no - * other alternative. - * - * @param version use ProtocolVersion from java.io.ObjectStreamConstants. - * @throws IllegalStateException if called after any objects - * have been serialized. - * @throws IllegalArgumentException if invalid version is passed in. - * @throws IOException if I/O errors occur - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2 - * @since 1.2 - */ - public void useProtocolVersion(int version) throws IOException { - if (handles.size() != 0) { - // REMIND: implement better check for pristine stream? - throw new IllegalStateException("stream non-empty"); - } - switch (version) { - case PROTOCOL_VERSION_1: - case PROTOCOL_VERSION_2: - protocol = version; - break; - - default: - throw new IllegalArgumentException( - "unknown version: " + version); - } - } - - /** - * Write the specified object to the ObjectOutputStream. The class of the - * object, the signature of the class, and the values of the non-transient - * and non-static fields of the class and all of its supertypes are - * written. Default serialization for a class can be overridden using the - * writeObject and the readObject methods. Objects referenced by this - * object are written transitively so that a complete equivalent graph of - * objects can be reconstructed by an ObjectInputStream. - * - *
Exceptions are thrown for problems with the OutputStream and for - * classes that should not be serialized. All exceptions are fatal to the - * OutputStream, which is left in an indeterminate state, and it is up to - * the caller to ignore or recover the stream state. - * - * @throws InvalidClassException Something is wrong with a class used by - * serialization. - * @throws NotSerializableException Some object to be serialized does not - * implement the java.io.Serializable interface. - * @throws IOException Any exception thrown by the underlying - * OutputStream. - */ - public final void writeObject(Object obj) throws IOException { - if (enableOverride) { - writeObjectOverride(obj); - return; - } - try { - writeObject0(obj, false); - } catch (IOException ex) { - if (depth == 0) { - writeFatalException(ex); - } - throw ex; - } - } - - /** - * Method used by subclasses to override the default writeObject method. - * This method is called by trusted subclasses of ObjectInputStream that - * constructed ObjectInputStream using the protected no-arg constructor. - * The subclass is expected to provide an override method with the modifier - * "final". - * - * @param obj object to be written to the underlying stream - * @throws IOException if there are I/O errors while writing to the - * underlying stream - * @see #ObjectOutputStream() - * @see #writeObject(Object) - * @since 1.2 - */ - protected void writeObjectOverride(Object obj) throws IOException { - } - - /** - * Writes an "unshared" object to the ObjectOutputStream. This method is - * identical to writeObject, except that it always writes the given object - * as a new, unique object in the stream (as opposed to a back-reference - * pointing to a previously serialized instance). Specifically: - *
ObjectOutputStream subclasses which override this method can only be
- * constructed in security contexts possessing the
- * "enableSubclassImplementation" SerializablePermission; any attempt to
- * instantiate such a subclass without this permission will cause a
- * SecurityException to be thrown.
- *
- * @param obj object to write to stream
- * @throws NotSerializableException if an object in the graph to be
- * serialized does not implement the Serializable interface
- * @throws InvalidClassException if a problem exists with the class of an
- * object to be serialized
- * @throws IOException if an I/O error occurs during serialization
- * @since 1.4
- */
- public void writeUnshared(Object obj) throws IOException {
- try {
- writeObject0(obj, true);
- } catch (IOException ex) {
- if (depth == 0) {
- writeFatalException(ex);
- }
- throw ex;
- }
- }
-
- /**
- * Write the non-static and non-transient fields of the current class to
- * this stream. This may only be called from the writeObject method of the
- * class being serialized. It will throw the NotActiveException if it is
- * called otherwise.
- *
- * @throws IOException if I/O errors occur while writing to the underlying
- * OutputStream
- */
- public void defaultWriteObject() throws IOException {
- if ( curContext == null ) {
- throw new NotActiveException("not in call to writeObject");
- }
- Object curObj = null; // curContext.getObj();
- ObjectStreamClass curDesc = null; // curContext.getDesc();
- bout.setBlockDataMode(false);
- defaultWriteFields(curObj, curDesc);
- bout.setBlockDataMode(true);
- }
-
- /**
- * Retrieve the object used to buffer persistent fields to be written to
- * the stream. The fields will be written to the stream when writeFields
- * method is called.
- *
- * @return an instance of the class Putfield that holds the serializable
- * fields
- * @throws IOException if I/O errors occur
- * @since 1.2
- */
- public ObjectOutputStream.PutField putFields() throws IOException {
- if (curPut == null) {
- if (curContext == null) {
- throw new NotActiveException("not in call to writeObject");
- }
- Object curObj = null; // curContext.getObj();
- ObjectStreamClass curDesc = null; // curContext.getDesc();
- curPut = new PutFieldImpl(curDesc);
- }
- return curPut;
- }
-
- /**
- * Write the buffered fields to the stream.
- *
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- * @throws NotActiveException Called when a classes writeObject method was
- * not called to write the state of the object.
- * @since 1.2
- */
- public void writeFields() throws IOException {
- if (curPut == null) {
- throw new NotActiveException("no current PutField object");
- }
- bout.setBlockDataMode(false);
- curPut.writeFields();
- bout.setBlockDataMode(true);
- }
-
- /**
- * Reset will disregard the state of any objects already written to the
- * stream. The state is reset to be the same as a new ObjectOutputStream.
- * The current point in the stream is marked as reset so the corresponding
- * ObjectInputStream will be reset at the same point. Objects previously
- * written to the stream will not be refered to as already being in the
- * stream. They will be written to the stream again.
- *
- * @throws IOException if reset() is invoked while serializing an object.
- */
- public void reset() throws IOException {
- if (depth != 0) {
- throw new IOException("stream active");
- }
- bout.setBlockDataMode(false);
- bout.writeByte(TC_RESET);
- clear();
- bout.setBlockDataMode(true);
- }
-
- /**
- * Subclasses may implement this method to allow class data to be stored in
- * the stream. By default this method does nothing. The corresponding
- * method in ObjectInputStream is resolveClass. This method is called
- * exactly once for each unique class in the stream. The class name and
- * signature will have already been written to the stream. This method may
- * make free use of the ObjectOutputStream to save any representation of
- * the class it deems suitable (for example, the bytes of the class file).
- * The resolveClass method in the corresponding subclass of
- * ObjectInputStream must read and use any data or objects written by
- * annotateClass.
- *
- * @param cl the class to annotate custom data for
- * @throws IOException Any exception thrown by the underlying
- * OutputStream.
- */
- protected void annotateClass(Class> cl) throws IOException {
- }
-
- /**
- * Subclasses may implement this method to store custom data in the stream
- * along with descriptors for dynamic proxy classes.
- *
- *
This method is called exactly once for each unique proxy class
- * descriptor in the stream. The default implementation of this method in
- * ObjectOutputStream
does nothing.
- *
- *
The corresponding method in ObjectInputStream
is
- * resolveProxyClass
. For a given subclass of
- * ObjectOutputStream
that overrides this method, the
- * resolveProxyClass
method in the corresponding subclass of
- * ObjectInputStream
must read any data or objects written by
- * annotateProxyClass
.
- *
- * @param cl the proxy class to annotate custom data for
- * @throws IOException any exception thrown by the underlying
- * OutputStream
- * @see ObjectInputStream#resolveProxyClass(String[])
- * @since 1.3
- */
- protected void annotateProxyClass(Class> cl) throws IOException {
- }
-
- /**
- * This method will allow trusted subclasses of ObjectOutputStream to
- * substitute one object for another during serialization. Replacing
- * objects is disabled until enableReplaceObject is called. The
- * enableReplaceObject method checks that the stream requesting to do
- * replacement can be trusted. The first occurrence of each object written
- * into the serialization stream is passed to replaceObject. Subsequent
- * references to the object are replaced by the object returned by the
- * original call to replaceObject. To ensure that the private state of
- * objects is not unintentionally exposed, only trusted streams may use
- * replaceObject.
- *
- *
The ObjectOutputStream.writeObject method takes a parameter of type - * Object (as opposed to type Serializable) to allow for cases where - * non-serializable objects are replaced by serializable ones. - * - *
When a subclass is replacing objects it must insure that either a - * complementary substitution must be made during deserialization or that - * the substituted object is compatible with every field where the - * reference will be stored. Objects whose type is not a subclass of the - * type of the field or array element abort the serialization by raising an - * exception and the object is not be stored. - * - *
This method is called only once when each object is first - * encountered. All subsequent references to the object will be redirected - * to the new object. This method should return the object to be - * substituted or the original object. - * - *
Null can be returned as the object to be substituted, but may cause - * NullReferenceException in classes that contain references to the - * original object since they may be expecting an object instead of - * null. - * - * @param obj the object to be replaced - * @return the alternate object that replaced the specified one - * @throws IOException Any exception thrown by the underlying - * OutputStream. - */ - protected Object replaceObject(Object obj) throws IOException { - return obj; - } - - /** - * Enable the stream to do replacement of objects in the stream. When - * enabled, the replaceObject method is called for every object being - * serialized. - * - *
If enable
is true, and there is a security manager
- * installed, this method first calls the security manager's
- * checkPermission
method with a
- * SerializablePermission("enableSubstitution")
permission to
- * ensure it's ok to enable the stream to do replacement of objects in the
- * stream.
- *
- * @param enable boolean parameter to enable replacement of objects
- * @return the previous setting before this method was invoked
- * @throws SecurityException if a security manager exists and its
- * checkPermission
method denies enabling the stream
- * to do replacement of objects in the stream.
- * @see SecurityManager#checkPermission
- * @see java.io.SerializablePermission
- */
- protected boolean enableReplaceObject(boolean enable)
- throws SecurityException
- {
- throw new SecurityException();
- }
-
- /**
- * The writeStreamHeader method is provided so subclasses can append or
- * prepend their own header to the stream. It writes the magic number and
- * version to the stream.
- *
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- protected void writeStreamHeader() throws IOException {
- bout.writeShort(STREAM_MAGIC);
- bout.writeShort(STREAM_VERSION);
- }
-
- /**
- * Write the specified class descriptor to the ObjectOutputStream. Class
- * descriptors are used to identify the classes of objects written to the
- * stream. Subclasses of ObjectOutputStream may override this method to
- * customize the way in which class descriptors are written to the
- * serialization stream. The corresponding method in ObjectInputStream,
- * readClassDescriptor
, should then be overridden to
- * reconstitute the class descriptor from its custom stream representation.
- * By default, this method writes class descriptors according to the format
- * defined in the Object Serialization specification.
- *
- *
Note that this method will only be called if the ObjectOutputStream
- * is not using the old serialization stream format (set by calling
- * ObjectOutputStream's useProtocolVersion
method). If this
- * serialization stream is using the old format
- * (PROTOCOL_VERSION_1
), the class descriptor will be written
- * internally in a manner that cannot be overridden or customized.
- *
- * @param desc class descriptor to write to the stream
- * @throws IOException If an I/O error has occurred.
- * @see java.io.ObjectInputStream#readClassDescriptor()
- * @see #useProtocolVersion(int)
- * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
- * @since 1.3
- */
- protected void writeClassDescriptor(ObjectStreamClass desc)
- throws IOException
- {
- desc.writeNonProxy(this);
- }
-
- /**
- * Writes a byte. This method will block until the byte is actually
- * written.
- *
- * @param val the byte to be written to the stream
- * @throws IOException If an I/O error has occurred.
- */
- public void write(int val) throws IOException {
- bout.write(val);
- }
-
- /**
- * Writes an array of bytes. This method will block until the bytes are
- * actually written.
- *
- * @param buf the data to be written
- * @throws IOException If an I/O error has occurred.
- */
- public void write(byte[] buf) throws IOException {
- bout.write(buf, 0, buf.length, false);
- }
-
- /**
- * Writes a sub array of bytes.
- *
- * @param buf the data to be written
- * @param off the start offset in the data
- * @param len the number of bytes that are written
- * @throws IOException If an I/O error has occurred.
- */
- public void write(byte[] buf, int off, int len) throws IOException {
- if (buf == null) {
- throw new NullPointerException();
- }
- int endoff = off + len;
- if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
- throw new IndexOutOfBoundsException();
- }
- bout.write(buf, off, len, false);
- }
-
- /**
- * Flushes the stream. This will write any buffered output bytes and flush
- * through to the underlying stream.
- *
- * @throws IOException If an I/O error has occurred.
- */
- public void flush() throws IOException {
- bout.flush();
- }
-
- /**
- * Drain any buffered data in ObjectOutputStream. Similar to flush but
- * does not propagate the flush to the underlying stream.
- *
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- protected void drain() throws IOException {
- bout.drain();
- }
-
- /**
- * Closes the stream. This method must be called to release any resources
- * associated with the stream.
- *
- * @throws IOException If an I/O error has occurred.
- */
- public void close() throws IOException {
- flush();
- clear();
- bout.close();
- }
-
- /**
- * Writes a boolean.
- *
- * @param val the boolean to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeBoolean(boolean val) throws IOException {
- bout.writeBoolean(val);
- }
-
- /**
- * Writes an 8 bit byte.
- *
- * @param val the byte value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeByte(int val) throws IOException {
- bout.writeByte(val);
- }
-
- /**
- * Writes a 16 bit short.
- *
- * @param val the short value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeShort(int val) throws IOException {
- bout.writeShort(val);
- }
-
- /**
- * Writes a 16 bit char.
- *
- * @param val the char value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeChar(int val) throws IOException {
- bout.writeChar(val);
- }
-
- /**
- * Writes a 32 bit int.
- *
- * @param val the integer value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeInt(int val) throws IOException {
- bout.writeInt(val);
- }
-
- /**
- * Writes a 64 bit long.
- *
- * @param val the long value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeLong(long val) throws IOException {
- bout.writeLong(val);
- }
-
- /**
- * Writes a 32 bit float.
- *
- * @param val the float value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeFloat(float val) throws IOException {
- bout.writeFloat(val);
- }
-
- /**
- * Writes a 64 bit double.
- *
- * @param val the double value to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeDouble(double val) throws IOException {
- bout.writeDouble(val);
- }
-
- /**
- * Writes a String as a sequence of bytes.
- *
- * @param str the String of bytes to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeBytes(String str) throws IOException {
- bout.writeBytes(str);
- }
-
- /**
- * Writes a String as a sequence of chars.
- *
- * @param str the String of chars to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeChars(String str) throws IOException {
- bout.writeChars(str);
- }
-
- /**
- * Primitive data write of this String in
- * modified UTF-8
- * format. Note that there is a
- * significant difference between writing a String into the stream as
- * primitive data or as an Object. A String instance written by writeObject
- * is written into the stream as a String initially. Future writeObject()
- * calls write references to the string into the stream.
- *
- * @param str the String to be written
- * @throws IOException if I/O errors occur while writing to the underlying
- * stream
- */
- public void writeUTF(String str) throws IOException {
- bout.writeUTF(str);
- }
-
- /**
- * Provide programmatic access to the persistent fields to be written
- * to ObjectOutput.
- *
- * @since 1.2
- */
- public static abstract class PutField {
-
- /**
- * Put the value of the named boolean field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * boolean
- */
- public abstract void put(String name, boolean val);
-
- /**
- * Put the value of the named byte field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * byte
- */
- public abstract void put(String name, byte val);
-
- /**
- * Put the value of the named char field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * char
- */
- public abstract void put(String name, char val);
-
- /**
- * Put the value of the named short field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * short
- */
- public abstract void put(String name, short val);
-
- /**
- * Put the value of the named int field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * int
- */
- public abstract void put(String name, int val);
-
- /**
- * Put the value of the named long field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * long
- */
- public abstract void put(String name, long val);
-
- /**
- * Put the value of the named float field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * float
- */
- public abstract void put(String name, float val);
-
- /**
- * Put the value of the named double field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not
- * double
- */
- public abstract void put(String name, double val);
-
- /**
- * Put the value of the named Object field into the persistent field.
- *
- * @param name the name of the serializable field
- * @param val the value to assign to the field
- * (which may be null
)
- * @throws IllegalArgumentException if name
does not
- * match the name of a serializable field for the class whose fields
- * are being written, or if the type of the named field is not a
- * reference type
- */
- public abstract void put(String name, Object val);
-
- /**
- * Write the data and fields to the specified ObjectOutput stream,
- * which must be the same stream that produced this
- * PutField
object.
- *
- * @param out the stream to write the data and fields to
- * @throws IOException if I/O errors occur while writing to the
- * underlying stream
- * @throws IllegalArgumentException if the specified stream is not
- * the same stream that produced this PutField
- * object
- * @deprecated This method does not write the values contained by this
- * PutField
object in a proper format, and may
- * result in corruption of the serialization stream. The
- * correct way to write PutField
data is by
- * calling the {@link java.io.ObjectOutputStream#writeFields()}
- * method.
- */
- @Deprecated
- public abstract void write(ObjectOutput out) throws IOException;
- }
-
-
- /**
- * Returns protocol version in use.
- */
- int getProtocolVersion() {
- return protocol;
- }
-
- /**
- * Writes string without allowing it to be replaced in stream. Used by
- * ObjectStreamClass to write class descriptor type strings.
- */
- void writeTypeString(String str) throws IOException {
- int handle;
- if (str == null) {
- writeNull();
- } else if ((handle = handles.lookup(str)) != -1) {
- writeHandle(handle);
- } else {
- writeString(str, false);
- }
- }
-
- /**
- * Verifies that this (possibly subclass) instance can be constructed
- * without violating security constraints: the subclass must not override
- * security-sensitive non-final methods, or else the
- * "enableSubclassImplementation" SerializablePermission is checked.
- */
- private void verifySubclass() {
- Class cl = getClass();
- if (cl == ObjectOutputStream.class) {
- return;
- }
- throw new SecurityException();
- }
-
- /**
- * Clears internal data structures.
- */
- private void clear() {
- subs.clear();
- handles.clear();
- }
-
- /**
- * Underlying writeObject/writeUnshared implementation.
- */
- private void writeObject0(Object obj, boolean unshared)
- throws IOException
- {
- boolean oldMode = bout.setBlockDataMode(false);
- depth++;
- try {
- // handle previously written and non-replaceable objects
- int h;
- if ((obj = subs.lookup(obj)) == null) {
- writeNull();
- return;
- } else if (!unshared && (h = handles.lookup(obj)) != -1) {
- writeHandle(h);
- return;
- } else if (obj instanceof Class) {
- writeClass((Class) obj, unshared);
- return;
- } else if (obj instanceof ObjectStreamClass) {
- writeClassDesc((ObjectStreamClass) obj, unshared);
- return;
- }
-
- // check for replacement object
- Object orig = obj;
- Class cl = obj.getClass();
- ObjectStreamClass desc;
- for (;;) {
- // REMIND: skip this check for strings/arrays?
- Class repCl;
- desc = ObjectStreamClass.lookup(cl, true);
- if (!desc.hasWriteReplaceMethod() ||
- (obj = desc.invokeWriteReplace(obj)) == null ||
- (repCl = obj.getClass()) == cl)
- {
- break;
- }
- cl = repCl;
- }
- if (enableReplace) {
- Object rep = replaceObject(obj);
- if (rep != obj && rep != null) {
- cl = rep.getClass();
- desc = ObjectStreamClass.lookup(cl, true);
- }
- obj = rep;
- }
-
- // if object replaced, run through original checks a second time
- if (obj != orig) {
- subs.assign(orig, obj);
- if (obj == null) {
- writeNull();
- return;
- } else if (!unshared && (h = handles.lookup(obj)) != -1) {
- writeHandle(h);
- return;
- } else if (obj instanceof Class) {
- writeClass((Class) obj, unshared);
- return;
- } else if (obj instanceof ObjectStreamClass) {
- writeClassDesc((ObjectStreamClass) obj, unshared);
- return;
- }
- }
-
- // remaining cases
- if (obj instanceof String) {
- writeString((String) obj, unshared);
- } else if (cl.isArray()) {
- writeArray(obj, desc, unshared);
- } else if (obj instanceof Enum) {
- writeEnum((Enum) obj, desc, unshared);
- } else if (obj instanceof Serializable) {
- writeOrdinaryObject(obj, desc, unshared);
- } else {
- if (extendedDebugInfo) {
- throw new NotSerializableException(
- cl.getName() + "\n" + debugInfoStack.toString());
- } else {
- throw new NotSerializableException(cl.getName());
- }
- }
- } finally {
- depth--;
- bout.setBlockDataMode(oldMode);
- }
- }
-
- /**
- * Writes null code to stream.
- */
- private void writeNull() throws IOException {
- bout.writeByte(TC_NULL);
- }
-
- /**
- * Writes given object handle to stream.
- */
- private void writeHandle(int handle) throws IOException {
- bout.writeByte(TC_REFERENCE);
- bout.writeInt(baseWireHandle + handle);
- }
-
- /**
- * Writes representation of given class to stream.
- */
- private void writeClass(Class cl, boolean unshared) throws IOException {
- bout.writeByte(TC_CLASS);
- writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
- handles.assign(unshared ? null : cl);
- }
-
- /**
- * Writes representation of given class descriptor to stream.
- */
- private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- int handle;
- if (desc == null) {
- writeNull();
- } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
- writeHandle(handle);
- } else if (desc.isProxy()) {
- writeProxyDesc(desc, unshared);
- } else {
- writeNonProxyDesc(desc, unshared);
- }
- }
-
- /**
- * Writes class descriptor representing a dynamic proxy class to stream.
- */
- private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_PROXYCLASSDESC);
- handles.assign(unshared ? null : desc);
-
- Class cl = desc.forClass();
- Class[] ifaces = cl.getInterfaces();
- bout.writeInt(ifaces.length);
- for (int i = 0; i < ifaces.length; i++) {
- bout.writeUTF(ifaces[i].getName());
- }
-
- bout.setBlockDataMode(true);
- annotateProxyClass(cl);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
-
- writeClassDesc(desc.getSuperDesc(), false);
- }
-
- /**
- * Writes class descriptor representing a standard (i.e., not a dynamic
- * proxy) class to stream.
- */
- private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_CLASSDESC);
- handles.assign(unshared ? null : desc);
-
- if (protocol == PROTOCOL_VERSION_1) {
- // do not invoke class descriptor write hook with old protocol
- desc.writeNonProxy(this);
- } else {
- writeClassDescriptor(desc);
- }
-
- Class cl = desc.forClass();
- bout.setBlockDataMode(true);
- annotateClass(cl);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
-
- writeClassDesc(desc.getSuperDesc(), false);
- }
-
- /**
- * Writes given string to stream, using standard or long UTF format
- * depending on string length.
- */
- private void writeString(String str, boolean unshared) throws IOException {
- handles.assign(unshared ? null : str);
- long utflen = bout.getUTFLength(str);
- if (utflen <= 0xFFFF) {
- bout.writeByte(TC_STRING);
- bout.writeUTF(str, utflen);
- } else {
- bout.writeByte(TC_LONGSTRING);
- bout.writeLongUTF(str, utflen);
- }
- }
-
- /**
- * Writes given array object to stream.
- */
- private void writeArray(Object array,
- ObjectStreamClass desc,
- boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_ARRAY);
- writeClassDesc(desc, false);
- handles.assign(unshared ? null : array);
-
- Class ccl = desc.forClass().getComponentType();
- if (ccl.isPrimitive()) {
- if (ccl == Integer.TYPE) {
- int[] ia = (int[]) array;
- bout.writeInt(ia.length);
- bout.writeInts(ia, 0, ia.length);
- } else if (ccl == Byte.TYPE) {
- byte[] ba = (byte[]) array;
- bout.writeInt(ba.length);
- bout.write(ba, 0, ba.length, true);
- } else if (ccl == Long.TYPE) {
- long[] ja = (long[]) array;
- bout.writeInt(ja.length);
- bout.writeLongs(ja, 0, ja.length);
- } else if (ccl == Float.TYPE) {
- float[] fa = (float[]) array;
- bout.writeInt(fa.length);
- bout.writeFloats(fa, 0, fa.length);
- } else if (ccl == Double.TYPE) {
- double[] da = (double[]) array;
- bout.writeInt(da.length);
- bout.writeDoubles(da, 0, da.length);
- } else if (ccl == Short.TYPE) {
- short[] sa = (short[]) array;
- bout.writeInt(sa.length);
- bout.writeShorts(sa, 0, sa.length);
- } else if (ccl == Character.TYPE) {
- char[] ca = (char[]) array;
- bout.writeInt(ca.length);
- bout.writeChars(ca, 0, ca.length);
- } else if (ccl == Boolean.TYPE) {
- boolean[] za = (boolean[]) array;
- bout.writeInt(za.length);
- bout.writeBooleans(za, 0, za.length);
- } else {
- throw new InternalError();
- }
- } else {
- Object[] objs = (Object[]) array;
- int len = objs.length;
- bout.writeInt(len);
- if (extendedDebugInfo) {
- debugInfoStack.push(
- "array (class \"" + array.getClass().getName() +
- "\", size: " + len + ")");
- }
- try {
- for (int i = 0; i < len; i++) {
- if (extendedDebugInfo) {
- debugInfoStack.push(
- "element of array (index: " + i + ")");
- }
- try {
- writeObject0(objs[i], false);
- } finally {
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- }
- } finally {
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- }
- }
-
- /**
- * Writes given enum constant to stream.
- */
- private void writeEnum(Enum en,
- ObjectStreamClass desc,
- boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_ENUM);
- ObjectStreamClass sdesc = desc.getSuperDesc();
- writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
- handles.assign(unshared ? null : en);
- writeString(en.name(), false);
- }
-
- /**
- * Writes representation of a "ordinary" (i.e., not a String, Class,
- * ObjectStreamClass, array, or enum constant) serializable object to the
- * stream.
- */
- private void writeOrdinaryObject(Object obj,
- ObjectStreamClass desc,
- boolean unshared)
- throws IOException
- {
- if (extendedDebugInfo) {
- debugInfoStack.push(
- (depth == 1 ? "root " : "") + "object (class \"" +
- obj.getClass().getName() + "\", " + obj.toString() + ")");
- }
- try {
- desc.checkSerialize();
-
- bout.writeByte(TC_OBJECT);
- writeClassDesc(desc, false);
- handles.assign(unshared ? null : obj);
- if (desc.isExternalizable() && !desc.isProxy()) {
- writeExternalData((Externalizable) obj);
- } else {
- writeSerialData(obj, desc);
- }
- } finally {
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- }
-
- /**
- * Writes externalizable data of given object by invoking its
- * writeExternal() method.
- */
- private void writeExternalData(Externalizable obj) throws IOException {
- PutFieldImpl oldPut = curPut;
- curPut = null;
-
- if (extendedDebugInfo) {
- debugInfoStack.push("writeExternal data");
- }
- Object oldContext = curContext;
- try {
- curContext = null;
- if (protocol == PROTOCOL_VERSION_1) {
- obj.writeExternal(this);
- } else {
- bout.setBlockDataMode(true);
- obj.writeExternal(this);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
- }
- } finally {
- curContext = oldContext;
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
-
- curPut = oldPut;
- }
-
- /**
- * Writes instance data for each serializable class of given object, from
- * superclass to subclass.
- */
- private void writeSerialData(Object obj, ObjectStreamClass desc)
- throws IOException
- {
- ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
- for (int i = 0; i < slots.length; i++) {
- ObjectStreamClass slotDesc = slots[i].desc;
- if (slotDesc.hasWriteObjectMethod()) {
- PutFieldImpl oldPut = curPut;
- curPut = null;
- Object oldContext = curContext;
-
- if (extendedDebugInfo) {
- debugInfoStack.push(
- "custom writeObject data (class \"" +
- slotDesc.getName() + "\")");
- }
- try {
- curContext = new Object(); //new SerialCallbackContext(obj, slotDesc);
- bout.setBlockDataMode(true);
- slotDesc.invokeWriteObject(obj, this);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
- } finally {
- //curContext.setUsed();
- curContext = oldContext;
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
-
- curPut = oldPut;
- } else {
- defaultWriteFields(obj, slotDesc);
- }
- }
- }
-
- /**
- * Fetches and writes values of serializable fields of given object to
- * stream. The given class descriptor specifies which field values to
- * write, and in which order they should be written.
- */
- private void defaultWriteFields(Object obj, ObjectStreamClass desc)
- throws IOException
- {
- // REMIND: perform conservative isInstance check here?
- desc.checkDefaultSerialize();
-
- int primDataSize = desc.getPrimDataSize();
- if (primVals == null || primVals.length < primDataSize) {
- primVals = new byte[primDataSize];
- }
- desc.getPrimFieldValues(obj, primVals);
- bout.write(primVals, 0, primDataSize, false);
-
- ObjectStreamField[] fields = desc.getFields(false);
- Object[] objVals = new Object[desc.getNumObjFields()];
- int numPrimFields = fields.length - objVals.length;
- desc.getObjFieldValues(obj, objVals);
- for (int i = 0; i < objVals.length; i++) {
- if (extendedDebugInfo) {
- debugInfoStack.push(
- "field (class \"" + desc.getName() + "\", name: \"" +
- fields[numPrimFields + i].getName() + "\", type: \"" +
- fields[numPrimFields + i].getType() + "\")");
- }
- try {
- writeObject0(objVals[i],
- fields[numPrimFields + i].isUnshared());
- } finally {
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- }
- }
-
- /**
- * Attempts to write to stream fatal IOException that has caused
- * serialization to abort.
- */
- private void writeFatalException(IOException ex) throws IOException {
- /*
- * Note: the serialization specification states that if a second
- * IOException occurs while attempting to serialize the original fatal
- * exception to the stream, then a StreamCorruptedException should be
- * thrown (section 2.1). However, due to a bug in previous
- * implementations of serialization, StreamCorruptedExceptions were
- * rarely (if ever) actually thrown--the "root" exceptions from
- * underlying streams were thrown instead. This historical behavior is
- * followed here for consistency.
- */
- clear();
- boolean oldMode = bout.setBlockDataMode(false);
- try {
- bout.writeByte(TC_EXCEPTION);
- writeObject0(ex, false);
- clear();
- } finally {
- bout.setBlockDataMode(oldMode);
- }
- }
-
- /**
- * Converts specified span of float values into byte values.
- */
- // REMIND: remove once hotspot inlines Float.floatToIntBits
- private static native void floatsToBytes(float[] src, int srcpos,
- byte[] dst, int dstpos,
- int nfloats);
-
- /**
- * Converts specified span of double values into byte values.
- */
- // REMIND: remove once hotspot inlines Double.doubleToLongBits
- private static native void doublesToBytes(double[] src, int srcpos,
- byte[] dst, int dstpos,
- int ndoubles);
-
- /**
- * Default PutField implementation.
- */
- private class PutFieldImpl extends PutField {
-
- /** class descriptor describing serializable fields */
- private final ObjectStreamClass desc;
- /** primitive field values */
- private final byte[] primVals;
- /** object field values */
- private final Object[] objVals;
-
- /**
- * Creates PutFieldImpl object for writing fields defined in given
- * class descriptor.
- */
- PutFieldImpl(ObjectStreamClass desc) {
- this.desc = desc;
- primVals = new byte[desc.getPrimDataSize()];
- objVals = new Object[desc.getNumObjFields()];
- }
-
- public void put(String name, boolean val) {
- Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
- }
-
- public void put(String name, byte val) {
- primVals[getFieldOffset(name, Byte.TYPE)] = val;
- }
-
- public void put(String name, char val) {
- Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
- }
-
- public void put(String name, short val) {
- Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
- }
-
- public void put(String name, int val) {
- Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
- }
-
- public void put(String name, float val) {
- Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
- }
-
- public void put(String name, long val) {
- Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
- }
-
- public void put(String name, double val) {
- Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
- }
-
- public void put(String name, Object val) {
- objVals[getFieldOffset(name, Object.class)] = val;
- }
-
- // deprecated in ObjectOutputStream.PutField
- public void write(ObjectOutput out) throws IOException {
- /*
- * Applications should *not* use this method to write PutField
- * data, as it will lead to stream corruption if the PutField
- * object writes any primitive data (since block data mode is not
- * unset/set properly, as is done in OOS.writeFields()). This
- * broken implementation is being retained solely for behavioral
- * compatibility, in order to support applications which use
- * OOS.PutField.write() for writing only non-primitive data.
- *
- * Serialization of unshared objects is not implemented here since
- * it is not necessary for backwards compatibility; also, unshared
- * semantics may not be supported by the given ObjectOutput
- * instance. Applications which write unshared objects using the
- * PutField API must use OOS.writeFields().
- */
- if (ObjectOutputStream.this != out) {
- throw new IllegalArgumentException("wrong stream");
- }
- out.write(primVals, 0, primVals.length);
-
- ObjectStreamField[] fields = desc.getFields(false);
- int numPrimFields = fields.length - objVals.length;
- // REMIND: warn if numPrimFields > 0?
- for (int i = 0; i < objVals.length; i++) {
- if (fields[numPrimFields + i].isUnshared()) {
- throw new IOException("cannot write unshared object");
- }
- out.writeObject(objVals[i]);
- }
- }
-
- /**
- * Writes buffered primitive data and object fields to stream.
- */
- void writeFields() throws IOException {
- bout.write(primVals, 0, primVals.length, false);
-
- ObjectStreamField[] fields = desc.getFields(false);
- int numPrimFields = fields.length - objVals.length;
- for (int i = 0; i < objVals.length; i++) {
- if (extendedDebugInfo) {
- debugInfoStack.push(
- "field (class \"" + desc.getName() + "\", name: \"" +
- fields[numPrimFields + i].getName() + "\", type: \"" +
- fields[numPrimFields + i].getType() + "\")");
- }
- try {
- writeObject0(objVals[i],
- fields[numPrimFields + i].isUnshared());
- } finally {
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- }
- }
-
- /**
- * Returns offset of field with given name and type. A specified type
- * of null matches all types, Object.class matches all non-primitive
- * types, and any other non-null type matches assignable types only.
- * Throws IllegalArgumentException if no matching field found.
- */
- private int getFieldOffset(String name, Class type) {
- ObjectStreamField field = desc.getField(name, type);
- if (field == null) {
- throw new IllegalArgumentException("no such field " + name +
- " with type " + type);
- }
- return field.getOffset();
- }
- }
-
- /**
- * Buffered output stream with two modes: in default mode, outputs data in
- * same format as DataOutputStream; in "block data" mode, outputs data
- * bracketed by block data markers (see object serialization specification
- * for details).
- */
- private static class BlockDataOutputStream
- extends OutputStream implements DataOutput
- {
- /** maximum data block length */
- private static final int MAX_BLOCK_SIZE = 1024;
- /** maximum data block header length */
- private static final int MAX_HEADER_SIZE = 5;
- /** (tunable) length of char buffer (for writing strings) */
- private static final int CHAR_BUF_SIZE = 256;
-
- /** buffer for writing general/block data */
- private final byte[] buf = new byte[MAX_BLOCK_SIZE];
- /** buffer for writing block data headers */
- private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
- /** char buffer for fast string writes */
- private final char[] cbuf = new char[CHAR_BUF_SIZE];
-
- /** block data mode */
- private boolean blkmode = false;
- /** current offset into buf */
- private int pos = 0;
-
- /** underlying output stream */
- private final OutputStream out;
- /** loopback stream (for data writes that span data blocks) */
- private final DataOutputStream dout;
-
- /**
- * Creates new BlockDataOutputStream on top of given underlying stream.
- * Block data mode is turned off by default.
- */
- BlockDataOutputStream(OutputStream out) {
- this.out = out;
- dout = new DataOutputStream(this);
- }
-
- /**
- * Sets block data mode to the given mode (true == on, false == off)
- * and returns the previous mode value. If the new mode is the same as
- * the old mode, no action is taken. If the new mode differs from the
- * old mode, any buffered data is flushed before switching to the new
- * mode.
- */
- boolean setBlockDataMode(boolean mode) throws IOException {
- if (blkmode == mode) {
- return blkmode;
- }
- drain();
- blkmode = mode;
- return !blkmode;
- }
-
- /**
- * Returns true if the stream is currently in block data mode, false
- * otherwise.
- */
- boolean getBlockDataMode() {
- return blkmode;
- }
-
- /* ----------------- generic output stream methods ----------------- */
- /*
- * The following methods are equivalent to their counterparts in
- * OutputStream, except that they partition written data into data
- * blocks when in block data mode.
- */
-
- public void write(int b) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- buf[pos++] = (byte) b;
- }
-
- public void write(byte[] b) throws IOException {
- write(b, 0, b.length, false);
- }
-
- public void write(byte[] b, int off, int len) throws IOException {
- write(b, off, len, false);
- }
-
- public void flush() throws IOException {
- drain();
- out.flush();
- }
-
- public void close() throws IOException {
- flush();
- out.close();
- }
-
- /**
- * Writes specified span of byte values from given array. If copy is
- * true, copies the values to an intermediate buffer before writing
- * them to underlying stream (to avoid exposing a reference to the
- * original byte array).
- */
- void write(byte[] b, int off, int len, boolean copy)
- throws IOException
- {
- if (!(copy || blkmode)) { // write directly
- drain();
- out.write(b, off, len);
- return;
- }
-
- while (len > 0) {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
- // avoid unnecessary copy
- writeBlockHeader(MAX_BLOCK_SIZE);
- out.write(b, off, MAX_BLOCK_SIZE);
- off += MAX_BLOCK_SIZE;
- len -= MAX_BLOCK_SIZE;
- } else {
- int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
- System.arraycopy(b, off, buf, pos, wlen);
- pos += wlen;
- off += wlen;
- len -= wlen;
- }
- }
- }
-
- /**
- * Writes all buffered data from this stream to the underlying stream,
- * but does not flush underlying stream.
- */
- void drain() throws IOException {
- if (pos == 0) {
- return;
- }
- if (blkmode) {
- writeBlockHeader(pos);
- }
- out.write(buf, 0, pos);
- pos = 0;
- }
-
- /**
- * Writes block data header. Data blocks shorter than 256 bytes are
- * prefixed with a 2-byte header; all others start with a 5-byte
- * header.
- */
- private void writeBlockHeader(int len) throws IOException {
- if (len <= 0xFF) {
- hbuf[0] = TC_BLOCKDATA;
- hbuf[1] = (byte) len;
- out.write(hbuf, 0, 2);
- } else {
- hbuf[0] = TC_BLOCKDATALONG;
- Bits.putInt(hbuf, 1, len);
- out.write(hbuf, 0, 5);
- }
- }
-
-
- /* ----------------- primitive data output methods ----------------- */
- /*
- * The following methods are equivalent to their counterparts in
- * DataOutputStream, except that they partition written data into data
- * blocks when in block data mode.
- */
-
- public void writeBoolean(boolean v) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- Bits.putBoolean(buf, pos++, v);
- }
-
- public void writeByte(int v) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- buf[pos++] = (byte) v;
- }
-
- public void writeChar(int v) throws IOException {
- if (pos + 2 <= MAX_BLOCK_SIZE) {
- Bits.putChar(buf, pos, (char) v);
- pos += 2;
- } else {
- dout.writeChar(v);
- }
- }
-
- public void writeShort(int v) throws IOException {
- if (pos + 2 <= MAX_BLOCK_SIZE) {
- Bits.putShort(buf, pos, (short) v);
- pos += 2;
- } else {
- dout.writeShort(v);
- }
- }
-
- public void writeInt(int v) throws IOException {
- if (pos + 4 <= MAX_BLOCK_SIZE) {
- Bits.putInt(buf, pos, v);
- pos += 4;
- } else {
- dout.writeInt(v);
- }
- }
-
- public void writeFloat(float v) throws IOException {
- if (pos + 4 <= MAX_BLOCK_SIZE) {
- Bits.putFloat(buf, pos, v);
- pos += 4;
- } else {
- dout.writeFloat(v);
- }
- }
-
- public void writeLong(long v) throws IOException {
- if (pos + 8 <= MAX_BLOCK_SIZE) {
- Bits.putLong(buf, pos, v);
- pos += 8;
- } else {
- dout.writeLong(v);
- }
- }
-
- public void writeDouble(double v) throws IOException {
- if (pos + 8 <= MAX_BLOCK_SIZE) {
- Bits.putDouble(buf, pos, v);
- pos += 8;
- } else {
- dout.writeDouble(v);
- }
- }
-
- public void writeBytes(String s) throws IOException {
- int endoff = s.length();
- int cpos = 0;
- int csize = 0;
- for (int off = 0; off < endoff; ) {
- if (cpos >= csize) {
- cpos = 0;
- csize = Math.min(endoff - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- }
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
- int stop = pos + n;
- while (pos < stop) {
- buf[pos++] = (byte) cbuf[cpos++];
- }
- off += n;
- }
- }
-
- public void writeChars(String s) throws IOException {
- int endoff = s.length();
- for (int off = 0; off < endoff; ) {
- int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- writeChars(cbuf, 0, csize);
- off += csize;
- }
- }
-
- public void writeUTF(String s) throws IOException {
- writeUTF(s, getUTFLength(s));
- }
-
-
- /* -------------- primitive data array output methods -------------- */
- /*
- * The following methods write out spans of primitive data values.
- * Though equivalent to calling the corresponding primitive write
- * methods repeatedly, these methods are optimized for writing groups
- * of primitive data values more efficiently.
- */
-
- void writeBooleans(boolean[] v, int off, int len) throws IOException {
- int endoff = off + len;
- while (off < endoff) {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
- while (off < stop) {
- Bits.putBoolean(buf, pos++, v[off++]);
- }
- }
- }
-
- void writeChars(char[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 2;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 1;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putChar(buf, pos, v[off++]);
- pos += 2;
- }
- } else {
- dout.writeChar(v[off++]);
- }
- }
- }
-
- void writeShorts(short[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 2;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 1;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putShort(buf, pos, v[off++]);
- pos += 2;
- }
- } else {
- dout.writeShort(v[off++]);
- }
- }
- }
-
- void writeInts(int[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 4;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 2;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putInt(buf, pos, v[off++]);
- pos += 4;
- }
- } else {
- dout.writeInt(v[off++]);
- }
- }
- }
-
- void writeFloats(float[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 4;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 2;
- int chunklen = Math.min(endoff - off, avail);
- floatsToBytes(v, off, buf, pos, chunklen);
- off += chunklen;
- pos += chunklen << 2;
- } else {
- dout.writeFloat(v[off++]);
- }
- }
- }
-
- void writeLongs(long[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 8;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 3;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putLong(buf, pos, v[off++]);
- pos += 8;
- }
- } else {
- dout.writeLong(v[off++]);
- }
- }
- }
-
- void writeDoubles(double[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 8;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 3;
- int chunklen = Math.min(endoff - off, avail);
- doublesToBytes(v, off, buf, pos, chunklen);
- off += chunklen;
- pos += chunklen << 3;
- } else {
- dout.writeDouble(v[off++]);
- }
- }
- }
-
- /**
- * Returns the length in bytes of the UTF encoding of the given string.
- */
- long getUTFLength(String s) {
- int len = s.length();
- long utflen = 0;
- for (int off = 0; off < len; ) {
- int csize = Math.min(len - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- for (int cpos = 0; cpos < csize; cpos++) {
- char c = cbuf[cpos];
- if (c >= 0x0001 && c <= 0x007F) {
- utflen++;
- } else if (c > 0x07FF) {
- utflen += 3;
- } else {
- utflen += 2;
- }
- }
- off += csize;
- }
- return utflen;
- }
-
- /**
- * Writes the given string in UTF format. This method is used in
- * situations where the UTF encoding length of the string is already
- * known; specifying it explicitly avoids a prescan of the string to
- * determine its UTF length.
- */
- void writeUTF(String s, long utflen) throws IOException {
- if (utflen > 0xFFFFL) {
- throw new UTFDataFormatException();
- }
- writeShort((int) utflen);
- if (utflen == (long) s.length()) {
- writeBytes(s);
- } else {
- writeUTFBody(s);
- }
- }
-
- /**
- * Writes given string in "long" UTF format. "Long" UTF format is
- * identical to standard UTF, except that it uses an 8 byte header
- * (instead of the standard 2 bytes) to convey the UTF encoding length.
- */
- void writeLongUTF(String s) throws IOException {
- writeLongUTF(s, getUTFLength(s));
- }
-
- /**
- * Writes given string in "long" UTF format, where the UTF encoding
- * length of the string is already known.
- */
- void writeLongUTF(String s, long utflen) throws IOException {
- writeLong(utflen);
- if (utflen == (long) s.length()) {
- writeBytes(s);
- } else {
- writeUTFBody(s);
- }
- }
-
- /**
- * Writes the "body" (i.e., the UTF representation minus the 2-byte or
- * 8-byte length header) of the UTF encoding for the given string.
- */
- private void writeUTFBody(String s) throws IOException {
- int limit = MAX_BLOCK_SIZE - 3;
- int len = s.length();
- for (int off = 0; off < len; ) {
- int csize = Math.min(len - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- for (int cpos = 0; cpos < csize; cpos++) {
- char c = cbuf[cpos];
- if (pos <= limit) {
- if (c <= 0x007F && c != 0) {
- buf[pos++] = (byte) c;
- } else if (c > 0x07FF) {
- buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
- buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
- buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
- pos += 3;
- } else {
- buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
- buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
- pos += 2;
- }
- } else { // write one byte at a time to normalize block
- if (c <= 0x007F && c != 0) {
- write(c);
- } else if (c > 0x07FF) {
- write(0xE0 | ((c >> 12) & 0x0F));
- write(0x80 | ((c >> 6) & 0x3F));
- write(0x80 | ((c >> 0) & 0x3F));
- } else {
- write(0xC0 | ((c >> 6) & 0x1F));
- write(0x80 | ((c >> 0) & 0x3F));
- }
- }
- }
- off += csize;
- }
- }
- }
-
- /**
- * Lightweight identity hash table which maps objects to integer handles,
- * assigned in ascending order.
- */
- private static class HandleTable {
-
- /* number of mappings in table/next available handle */
- private int size;
- /* size threshold determining when to expand hash spine */
- private int threshold;
- /* factor for computing size threshold */
- private final float loadFactor;
- /* maps hash value -> candidate handle value */
- private int[] spine;
- /* maps handle value -> next candidate handle value */
- private int[] next;
- /* maps handle value -> associated object */
- private Object[] objs;
-
- /**
- * Creates new HandleTable with given capacity and load factor.
- */
- HandleTable(int initialCapacity, float loadFactor) {
- this.loadFactor = loadFactor;
- spine = new int[initialCapacity];
- next = new int[initialCapacity];
- objs = new Object[initialCapacity];
- threshold = (int) (initialCapacity * loadFactor);
- clear();
- }
-
- /**
- * Assigns next available handle to given object, and returns handle
- * value. Handles are assigned in ascending order starting at 0.
- */
- int assign(Object obj) {
- if (size >= next.length) {
- growEntries();
- }
- if (size >= threshold) {
- growSpine();
- }
- insert(obj, size);
- return size++;
- }
-
- /**
- * Looks up and returns handle associated with given object, or -1 if
- * no mapping found.
- */
- int lookup(Object obj) {
- if (size == 0) {
- return -1;
- }
- int index = hash(obj) % spine.length;
- for (int i = spine[index]; i >= 0; i = next[i]) {
- if (objs[i] == obj) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Resets table to its initial (empty) state.
- */
- void clear() {
- Arrays.fill(spine, -1);
- Arrays.fill(objs, 0, size, null);
- size = 0;
- }
-
- /**
- * Returns the number of mappings currently in table.
- */
- int size() {
- return size;
- }
-
- /**
- * Inserts mapping object -> handle mapping into table. Assumes table
- * is large enough to accommodate new mapping.
- */
- private void insert(Object obj, int handle) {
- int index = hash(obj) % spine.length;
- objs[handle] = obj;
- next[handle] = spine[index];
- spine[index] = handle;
- }
-
- /**
- * Expands the hash "spine" -- equivalent to increasing the number of
- * buckets in a conventional hash table.
- */
- private void growSpine() {
- spine = new int[(spine.length << 1) + 1];
- threshold = (int) (spine.length * loadFactor);
- Arrays.fill(spine, -1);
- for (int i = 0; i < size; i++) {
- insert(objs[i], i);
- }
- }
-
- /**
- * Increases hash table capacity by lengthening entry arrays.
- */
- private void growEntries() {
- int newLength = (next.length << 1) + 1;
- int[] newNext = new int[newLength];
- System.arraycopy(next, 0, newNext, 0, size);
- next = newNext;
-
- Object[] newObjs = new Object[newLength];
- System.arraycopy(objs, 0, newObjs, 0, size);
- objs = newObjs;
- }
-
- /**
- * Returns hash value for given object.
- */
- private int hash(Object obj) {
- return System.identityHashCode(obj) & 0x7FFFFFFF;
- }
- }
-
- /**
- * Lightweight identity hash table which maps objects to replacement
- * objects.
- */
- private static class ReplaceTable {
-
- /* maps object -> index */
- private final HandleTable htab;
- /* maps index -> replacement object */
- private Object[] reps;
-
- /**
- * Creates new ReplaceTable with given capacity and load factor.
- */
- ReplaceTable(int initialCapacity, float loadFactor) {
- htab = new HandleTable(initialCapacity, loadFactor);
- reps = new Object[initialCapacity];
- }
-
- /**
- * Enters mapping from object to replacement object.
- */
- void assign(Object obj, Object rep) {
- int index = htab.assign(obj);
- while (index >= reps.length) {
- grow();
- }
- reps[index] = rep;
- }
-
- /**
- * Looks up and returns replacement for given object. If no
- * replacement is found, returns the lookup object itself.
- */
- Object lookup(Object obj) {
- int index = htab.lookup(obj);
- return (index >= 0) ? reps[index] : obj;
- }
-
- /**
- * Resets table to its initial (empty) state.
- */
- void clear() {
- Arrays.fill(reps, 0, htab.size(), null);
- htab.clear();
- }
-
- /**
- * Returns the number of mappings currently in table.
- */
- int size() {
- return htab.size();
- }
-
- /**
- * Increases table capacity.
- */
- private void grow() {
- Object[] newReps = new Object[(reps.length << 1) + 1];
- System.arraycopy(reps, 0, newReps, 0, reps.length);
- reps = newReps;
- }
- }
-
- /**
- * Stack to keep debug information about the state of the
- * serialization process, for embedding in exception messages.
- */
- private static class DebugTraceInfoStack {
- private final List