1.1 --- a/emul/compact/src/main/java/java/io/ObjectOutputStream.java Fri Mar 22 16:59:47 2013 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,2369 +0,0 @@
1.4 -/*
1.5 - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
1.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 - *
1.8 - * This code is free software; you can redistribute it and/or modify it
1.9 - * under the terms of the GNU General Public License version 2 only, as
1.10 - * published by the Free Software Foundation. Oracle designates this
1.11 - * particular file as subject to the "Classpath" exception as provided
1.12 - * by Oracle in the LICENSE file that accompanied this code.
1.13 - *
1.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 - * version 2 for more details (a copy is included in the LICENSE file that
1.18 - * accompanied this code).
1.19 - *
1.20 - * You should have received a copy of the GNU General Public License version
1.21 - * 2 along with this work; if not, write to the Free Software Foundation,
1.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 - *
1.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 - * or visit www.oracle.com if you need additional information or have any
1.26 - * questions.
1.27 - */
1.28 -
1.29 -package java.io;
1.30 -
1.31 -import java.util.ArrayList;
1.32 -import java.util.Arrays;
1.33 -import java.util.List;
1.34 -import org.apidesign.bck2brwsr.emul.lang.System;
1.35 -
1.36 -/**
1.37 - * An ObjectOutputStream writes primitive data types and graphs of Java objects
1.38 - * to an OutputStream. The objects can be read (reconstituted) using an
1.39 - * ObjectInputStream. Persistent storage of objects can be accomplished by
1.40 - * using a file for the stream. If the stream is a network socket stream, the
1.41 - * objects can be reconstituted on another host or in another process.
1.42 - *
1.43 - * <p>Only objects that support the java.io.Serializable interface can be
1.44 - * written to streams. The class of each serializable object is encoded
1.45 - * including the class name and signature of the class, the values of the
1.46 - * object's fields and arrays, and the closure of any other objects referenced
1.47 - * from the initial objects.
1.48 - *
1.49 - * <p>The method writeObject is used to write an object to the stream. Any
1.50 - * object, including Strings and arrays, is written with writeObject. Multiple
1.51 - * objects or primitives can be written to the stream. The objects must be
1.52 - * read back from the corresponding ObjectInputstream with the same types and
1.53 - * in the same order as they were written.
1.54 - *
1.55 - * <p>Primitive data types can also be written to the stream using the
1.56 - * appropriate methods from DataOutput. Strings can also be written using the
1.57 - * writeUTF method.
1.58 - *
1.59 - * <p>The default serialization mechanism for an object writes the class of the
1.60 - * object, the class signature, and the values of all non-transient and
1.61 - * non-static fields. References to other objects (except in transient or
1.62 - * static fields) cause those objects to be written also. Multiple references
1.63 - * to a single object are encoded using a reference sharing mechanism so that
1.64 - * graphs of objects can be restored to the same shape as when the original was
1.65 - * written.
1.66 - *
1.67 - * <p>For example to write an object that can be read by the example in
1.68 - * ObjectInputStream:
1.69 - * <br>
1.70 - * <pre>
1.71 - * FileOutputStream fos = new FileOutputStream("t.tmp");
1.72 - * ObjectOutputStream oos = new ObjectOutputStream(fos);
1.73 - *
1.74 - * oos.writeInt(12345);
1.75 - * oos.writeObject("Today");
1.76 - * oos.writeObject(new Date());
1.77 - *
1.78 - * oos.close();
1.79 - * </pre>
1.80 - *
1.81 - * <p>Classes that require special handling during the serialization and
1.82 - * deserialization process must implement special methods with these exact
1.83 - * signatures:
1.84 - * <br>
1.85 - * <pre>
1.86 - * private void readObject(java.io.ObjectInputStream stream)
1.87 - * throws IOException, ClassNotFoundException;
1.88 - * private void writeObject(java.io.ObjectOutputStream stream)
1.89 - * throws IOException
1.90 - * private void readObjectNoData()
1.91 - * throws ObjectStreamException;
1.92 - * </pre>
1.93 - *
1.94 - * <p>The writeObject method is responsible for writing the state of the object
1.95 - * for its particular class so that the corresponding readObject method can
1.96 - * restore it. The method does not need to concern itself with the state
1.97 - * belonging to the object's superclasses or subclasses. State is saved by
1.98 - * writing the individual fields to the ObjectOutputStream using the
1.99 - * writeObject method or by using the methods for primitive data types
1.100 - * supported by DataOutput.
1.101 - *
1.102 - * <p>Serialization does not write out the fields of any object that does not
1.103 - * implement the java.io.Serializable interface. Subclasses of Objects that
1.104 - * are not serializable can be serializable. In this case the non-serializable
1.105 - * class must have a no-arg constructor to allow its fields to be initialized.
1.106 - * In this case it is the responsibility of the subclass to save and restore
1.107 - * the state of the non-serializable class. It is frequently the case that the
1.108 - * fields of that class are accessible (public, package, or protected) or that
1.109 - * there are get and set methods that can be used to restore the state.
1.110 - *
1.111 - * <p>Serialization of an object can be prevented by implementing writeObject
1.112 - * and readObject methods that throw the NotSerializableException. The
1.113 - * exception will be caught by the ObjectOutputStream and abort the
1.114 - * serialization process.
1.115 - *
1.116 - * <p>Implementing the Externalizable interface allows the object to assume
1.117 - * complete control over the contents and format of the object's serialized
1.118 - * form. The methods of the Externalizable interface, writeExternal and
1.119 - * readExternal, are called to save and restore the objects state. When
1.120 - * implemented by a class they can write and read their own state using all of
1.121 - * the methods of ObjectOutput and ObjectInput. It is the responsibility of
1.122 - * the objects to handle any versioning that occurs.
1.123 - *
1.124 - * <p>Enum constants are serialized differently than ordinary serializable or
1.125 - * externalizable objects. The serialized form of an enum constant consists
1.126 - * solely of its name; field values of the constant are not transmitted. To
1.127 - * serialize an enum constant, ObjectOutputStream writes the string returned by
1.128 - * the constant's name method. Like other serializable or externalizable
1.129 - * objects, enum constants can function as the targets of back references
1.130 - * appearing subsequently in the serialization stream. The process by which
1.131 - * enum constants are serialized cannot be customized; any class-specific
1.132 - * writeObject and writeReplace methods defined by enum types are ignored
1.133 - * during serialization. Similarly, any serialPersistentFields or
1.134 - * serialVersionUID field declarations are also ignored--all enum types have a
1.135 - * fixed serialVersionUID of 0L.
1.136 - *
1.137 - * <p>Primitive data, excluding serializable fields and externalizable data, is
1.138 - * written to the ObjectOutputStream in block-data records. A block data record
1.139 - * is composed of a header and data. The block data header consists of a marker
1.140 - * and the number of bytes to follow the header. Consecutive primitive data
1.141 - * writes are merged into one block-data record. The blocking factor used for
1.142 - * a block-data record will be 1024 bytes. Each block-data record will be
1.143 - * filled up to 1024 bytes, or be written whenever there is a termination of
1.144 - * block-data mode. Calls to the ObjectOutputStream methods writeObject,
1.145 - * defaultWriteObject and writeFields initially terminate any existing
1.146 - * block-data record.
1.147 - *
1.148 - * @author Mike Warres
1.149 - * @author Roger Riggs
1.150 - * @see java.io.DataOutput
1.151 - * @see java.io.ObjectInputStream
1.152 - * @see java.io.Serializable
1.153 - * @see java.io.Externalizable
1.154 - * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
1.155 - * @since JDK1.1
1.156 - */
1.157 -public class ObjectOutputStream
1.158 - extends OutputStream implements ObjectOutput, ObjectStreamConstants
1.159 -{
1.160 - /** filter stream for handling block data conversion */
1.161 - private final BlockDataOutputStream bout;
1.162 - /** obj -> wire handle map */
1.163 - private final HandleTable handles;
1.164 - /** obj -> replacement obj map */
1.165 - private final ReplaceTable subs;
1.166 - /** stream protocol version */
1.167 - private int protocol = PROTOCOL_VERSION_2;
1.168 - /** recursion depth */
1.169 - private int depth;
1.170 -
1.171 - /** buffer for writing primitive field values */
1.172 - private byte[] primVals;
1.173 -
1.174 - /** if true, invoke writeObjectOverride() instead of writeObject() */
1.175 - private final boolean enableOverride;
1.176 - /** if true, invoke replaceObject() */
1.177 - private boolean enableReplace;
1.178 -
1.179 - // values below valid only during upcalls to writeObject()/writeExternal()
1.180 - /**
1.181 - * Context during upcalls to class-defined writeObject methods; holds
1.182 - * object currently being serialized and descriptor for current class.
1.183 - * Null when not during writeObject upcall.
1.184 - */
1.185 - private Object curContext;
1.186 - /** current PutField object */
1.187 - private PutFieldImpl curPut;
1.188 -
1.189 - /** custom storage for debug trace info */
1.190 - private final DebugTraceInfoStack debugInfoStack;
1.191 -
1.192 - /**
1.193 - * value of "sun.io.serialization.extendedDebugInfo" property,
1.194 - * as true or false for extended information about exception's place
1.195 - */
1.196 - private static final boolean extendedDebugInfo = false;
1.197 -
1.198 - /**
1.199 - * Creates an ObjectOutputStream that writes to the specified OutputStream.
1.200 - * This constructor writes the serialization stream header to the
1.201 - * underlying stream; callers may wish to flush the stream immediately to
1.202 - * ensure that constructors for receiving ObjectInputStreams will not block
1.203 - * when reading the header.
1.204 - *
1.205 - * <p>If a security manager is installed, this constructor will check for
1.206 - * the "enableSubclassImplementation" SerializablePermission when invoked
1.207 - * directly or indirectly by the constructor of a subclass which overrides
1.208 - * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
1.209 - * methods.
1.210 - *
1.211 - * @param out output stream to write to
1.212 - * @throws IOException if an I/O error occurs while writing stream header
1.213 - * @throws SecurityException if untrusted subclass illegally overrides
1.214 - * security-sensitive methods
1.215 - * @throws NullPointerException if <code>out</code> is <code>null</code>
1.216 - * @since 1.4
1.217 - * @see ObjectOutputStream#ObjectOutputStream()
1.218 - * @see ObjectOutputStream#putFields()
1.219 - * @see ObjectInputStream#ObjectInputStream(InputStream)
1.220 - */
1.221 - public ObjectOutputStream(OutputStream out) throws IOException {
1.222 - verifySubclass();
1.223 - bout = new BlockDataOutputStream(out);
1.224 - handles = new HandleTable(10, (float) 3.00);
1.225 - subs = new ReplaceTable(10, (float) 3.00);
1.226 - enableOverride = false;
1.227 - writeStreamHeader();
1.228 - bout.setBlockDataMode(true);
1.229 - if (extendedDebugInfo) {
1.230 - debugInfoStack = new DebugTraceInfoStack();
1.231 - } else {
1.232 - debugInfoStack = null;
1.233 - }
1.234 - }
1.235 -
1.236 - /**
1.237 - * Provide a way for subclasses that are completely reimplementing
1.238 - * ObjectOutputStream to not have to allocate private data just used by
1.239 - * this implementation of ObjectOutputStream.
1.240 - *
1.241 - * <p>If there is a security manager installed, this method first calls the
1.242 - * security manager's <code>checkPermission</code> method with a
1.243 - * <code>SerializablePermission("enableSubclassImplementation")</code>
1.244 - * permission to ensure it's ok to enable subclassing.
1.245 - *
1.246 - * @throws SecurityException if a security manager exists and its
1.247 - * <code>checkPermission</code> method denies enabling
1.248 - * subclassing.
1.249 - * @see SecurityManager#checkPermission
1.250 - * @see java.io.SerializablePermission
1.251 - */
1.252 - protected ObjectOutputStream() throws IOException, SecurityException {
1.253 - throw new SecurityException();
1.254 - }
1.255 -
1.256 - /**
1.257 - * Specify stream protocol version to use when writing the stream.
1.258 - *
1.259 - * <p>This routine provides a hook to enable the current version of
1.260 - * Serialization to write in a format that is backwards compatible to a
1.261 - * previous version of the stream format.
1.262 - *
1.263 - * <p>Every effort will be made to avoid introducing additional
1.264 - * backwards incompatibilities; however, sometimes there is no
1.265 - * other alternative.
1.266 - *
1.267 - * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
1.268 - * @throws IllegalStateException if called after any objects
1.269 - * have been serialized.
1.270 - * @throws IllegalArgumentException if invalid version is passed in.
1.271 - * @throws IOException if I/O errors occur
1.272 - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
1.273 - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
1.274 - * @since 1.2
1.275 - */
1.276 - public void useProtocolVersion(int version) throws IOException {
1.277 - if (handles.size() != 0) {
1.278 - // REMIND: implement better check for pristine stream?
1.279 - throw new IllegalStateException("stream non-empty");
1.280 - }
1.281 - switch (version) {
1.282 - case PROTOCOL_VERSION_1:
1.283 - case PROTOCOL_VERSION_2:
1.284 - protocol = version;
1.285 - break;
1.286 -
1.287 - default:
1.288 - throw new IllegalArgumentException(
1.289 - "unknown version: " + version);
1.290 - }
1.291 - }
1.292 -
1.293 - /**
1.294 - * Write the specified object to the ObjectOutputStream. The class of the
1.295 - * object, the signature of the class, and the values of the non-transient
1.296 - * and non-static fields of the class and all of its supertypes are
1.297 - * written. Default serialization for a class can be overridden using the
1.298 - * writeObject and the readObject methods. Objects referenced by this
1.299 - * object are written transitively so that a complete equivalent graph of
1.300 - * objects can be reconstructed by an ObjectInputStream.
1.301 - *
1.302 - * <p>Exceptions are thrown for problems with the OutputStream and for
1.303 - * classes that should not be serialized. All exceptions are fatal to the
1.304 - * OutputStream, which is left in an indeterminate state, and it is up to
1.305 - * the caller to ignore or recover the stream state.
1.306 - *
1.307 - * @throws InvalidClassException Something is wrong with a class used by
1.308 - * serialization.
1.309 - * @throws NotSerializableException Some object to be serialized does not
1.310 - * implement the java.io.Serializable interface.
1.311 - * @throws IOException Any exception thrown by the underlying
1.312 - * OutputStream.
1.313 - */
1.314 - public final void writeObject(Object obj) throws IOException {
1.315 - if (enableOverride) {
1.316 - writeObjectOverride(obj);
1.317 - return;
1.318 - }
1.319 - try {
1.320 - writeObject0(obj, false);
1.321 - } catch (IOException ex) {
1.322 - if (depth == 0) {
1.323 - writeFatalException(ex);
1.324 - }
1.325 - throw ex;
1.326 - }
1.327 - }
1.328 -
1.329 - /**
1.330 - * Method used by subclasses to override the default writeObject method.
1.331 - * This method is called by trusted subclasses of ObjectInputStream that
1.332 - * constructed ObjectInputStream using the protected no-arg constructor.
1.333 - * The subclass is expected to provide an override method with the modifier
1.334 - * "final".
1.335 - *
1.336 - * @param obj object to be written to the underlying stream
1.337 - * @throws IOException if there are I/O errors while writing to the
1.338 - * underlying stream
1.339 - * @see #ObjectOutputStream()
1.340 - * @see #writeObject(Object)
1.341 - * @since 1.2
1.342 - */
1.343 - protected void writeObjectOverride(Object obj) throws IOException {
1.344 - }
1.345 -
1.346 - /**
1.347 - * Writes an "unshared" object to the ObjectOutputStream. This method is
1.348 - * identical to writeObject, except that it always writes the given object
1.349 - * as a new, unique object in the stream (as opposed to a back-reference
1.350 - * pointing to a previously serialized instance). Specifically:
1.351 - * <ul>
1.352 - * <li>An object written via writeUnshared is always serialized in the
1.353 - * same manner as a newly appearing object (an object that has not
1.354 - * been written to the stream yet), regardless of whether or not the
1.355 - * object has been written previously.
1.356 - *
1.357 - * <li>If writeObject is used to write an object that has been previously
1.358 - * written with writeUnshared, the previous writeUnshared operation
1.359 - * is treated as if it were a write of a separate object. In other
1.360 - * words, ObjectOutputStream will never generate back-references to
1.361 - * object data written by calls to writeUnshared.
1.362 - * </ul>
1.363 - * While writing an object via writeUnshared does not in itself guarantee a
1.364 - * unique reference to the object when it is deserialized, it allows a
1.365 - * single object to be defined multiple times in a stream, so that multiple
1.366 - * calls to readUnshared by the receiver will not conflict. Note that the
1.367 - * rules described above only apply to the base-level object written with
1.368 - * writeUnshared, and not to any transitively referenced sub-objects in the
1.369 - * object graph to be serialized.
1.370 - *
1.371 - * <p>ObjectOutputStream subclasses which override this method can only be
1.372 - * constructed in security contexts possessing the
1.373 - * "enableSubclassImplementation" SerializablePermission; any attempt to
1.374 - * instantiate such a subclass without this permission will cause a
1.375 - * SecurityException to be thrown.
1.376 - *
1.377 - * @param obj object to write to stream
1.378 - * @throws NotSerializableException if an object in the graph to be
1.379 - * serialized does not implement the Serializable interface
1.380 - * @throws InvalidClassException if a problem exists with the class of an
1.381 - * object to be serialized
1.382 - * @throws IOException if an I/O error occurs during serialization
1.383 - * @since 1.4
1.384 - */
1.385 - public void writeUnshared(Object obj) throws IOException {
1.386 - try {
1.387 - writeObject0(obj, true);
1.388 - } catch (IOException ex) {
1.389 - if (depth == 0) {
1.390 - writeFatalException(ex);
1.391 - }
1.392 - throw ex;
1.393 - }
1.394 - }
1.395 -
1.396 - /**
1.397 - * Write the non-static and non-transient fields of the current class to
1.398 - * this stream. This may only be called from the writeObject method of the
1.399 - * class being serialized. It will throw the NotActiveException if it is
1.400 - * called otherwise.
1.401 - *
1.402 - * @throws IOException if I/O errors occur while writing to the underlying
1.403 - * <code>OutputStream</code>
1.404 - */
1.405 - public void defaultWriteObject() throws IOException {
1.406 - if ( curContext == null ) {
1.407 - throw new NotActiveException("not in call to writeObject");
1.408 - }
1.409 - Object curObj = null; // curContext.getObj();
1.410 - ObjectStreamClass curDesc = null; // curContext.getDesc();
1.411 - bout.setBlockDataMode(false);
1.412 - defaultWriteFields(curObj, curDesc);
1.413 - bout.setBlockDataMode(true);
1.414 - }
1.415 -
1.416 - /**
1.417 - * Retrieve the object used to buffer persistent fields to be written to
1.418 - * the stream. The fields will be written to the stream when writeFields
1.419 - * method is called.
1.420 - *
1.421 - * @return an instance of the class Putfield that holds the serializable
1.422 - * fields
1.423 - * @throws IOException if I/O errors occur
1.424 - * @since 1.2
1.425 - */
1.426 - public ObjectOutputStream.PutField putFields() throws IOException {
1.427 - if (curPut == null) {
1.428 - if (curContext == null) {
1.429 - throw new NotActiveException("not in call to writeObject");
1.430 - }
1.431 - Object curObj = null; // curContext.getObj();
1.432 - ObjectStreamClass curDesc = null; // curContext.getDesc();
1.433 - curPut = new PutFieldImpl(curDesc);
1.434 - }
1.435 - return curPut;
1.436 - }
1.437 -
1.438 - /**
1.439 - * Write the buffered fields to the stream.
1.440 - *
1.441 - * @throws IOException if I/O errors occur while writing to the underlying
1.442 - * stream
1.443 - * @throws NotActiveException Called when a classes writeObject method was
1.444 - * not called to write the state of the object.
1.445 - * @since 1.2
1.446 - */
1.447 - public void writeFields() throws IOException {
1.448 - if (curPut == null) {
1.449 - throw new NotActiveException("no current PutField object");
1.450 - }
1.451 - bout.setBlockDataMode(false);
1.452 - curPut.writeFields();
1.453 - bout.setBlockDataMode(true);
1.454 - }
1.455 -
1.456 - /**
1.457 - * Reset will disregard the state of any objects already written to the
1.458 - * stream. The state is reset to be the same as a new ObjectOutputStream.
1.459 - * The current point in the stream is marked as reset so the corresponding
1.460 - * ObjectInputStream will be reset at the same point. Objects previously
1.461 - * written to the stream will not be refered to as already being in the
1.462 - * stream. They will be written to the stream again.
1.463 - *
1.464 - * @throws IOException if reset() is invoked while serializing an object.
1.465 - */
1.466 - public void reset() throws IOException {
1.467 - if (depth != 0) {
1.468 - throw new IOException("stream active");
1.469 - }
1.470 - bout.setBlockDataMode(false);
1.471 - bout.writeByte(TC_RESET);
1.472 - clear();
1.473 - bout.setBlockDataMode(true);
1.474 - }
1.475 -
1.476 - /**
1.477 - * Subclasses may implement this method to allow class data to be stored in
1.478 - * the stream. By default this method does nothing. The corresponding
1.479 - * method in ObjectInputStream is resolveClass. This method is called
1.480 - * exactly once for each unique class in the stream. The class name and
1.481 - * signature will have already been written to the stream. This method may
1.482 - * make free use of the ObjectOutputStream to save any representation of
1.483 - * the class it deems suitable (for example, the bytes of the class file).
1.484 - * The resolveClass method in the corresponding subclass of
1.485 - * ObjectInputStream must read and use any data or objects written by
1.486 - * annotateClass.
1.487 - *
1.488 - * @param cl the class to annotate custom data for
1.489 - * @throws IOException Any exception thrown by the underlying
1.490 - * OutputStream.
1.491 - */
1.492 - protected void annotateClass(Class<?> cl) throws IOException {
1.493 - }
1.494 -
1.495 - /**
1.496 - * Subclasses may implement this method to store custom data in the stream
1.497 - * along with descriptors for dynamic proxy classes.
1.498 - *
1.499 - * <p>This method is called exactly once for each unique proxy class
1.500 - * descriptor in the stream. The default implementation of this method in
1.501 - * <code>ObjectOutputStream</code> does nothing.
1.502 - *
1.503 - * <p>The corresponding method in <code>ObjectInputStream</code> is
1.504 - * <code>resolveProxyClass</code>. For a given subclass of
1.505 - * <code>ObjectOutputStream</code> that overrides this method, the
1.506 - * <code>resolveProxyClass</code> method in the corresponding subclass of
1.507 - * <code>ObjectInputStream</code> must read any data or objects written by
1.508 - * <code>annotateProxyClass</code>.
1.509 - *
1.510 - * @param cl the proxy class to annotate custom data for
1.511 - * @throws IOException any exception thrown by the underlying
1.512 - * <code>OutputStream</code>
1.513 - * @see ObjectInputStream#resolveProxyClass(String[])
1.514 - * @since 1.3
1.515 - */
1.516 - protected void annotateProxyClass(Class<?> cl) throws IOException {
1.517 - }
1.518 -
1.519 - /**
1.520 - * This method will allow trusted subclasses of ObjectOutputStream to
1.521 - * substitute one object for another during serialization. Replacing
1.522 - * objects is disabled until enableReplaceObject is called. The
1.523 - * enableReplaceObject method checks that the stream requesting to do
1.524 - * replacement can be trusted. The first occurrence of each object written
1.525 - * into the serialization stream is passed to replaceObject. Subsequent
1.526 - * references to the object are replaced by the object returned by the
1.527 - * original call to replaceObject. To ensure that the private state of
1.528 - * objects is not unintentionally exposed, only trusted streams may use
1.529 - * replaceObject.
1.530 - *
1.531 - * <p>The ObjectOutputStream.writeObject method takes a parameter of type
1.532 - * Object (as opposed to type Serializable) to allow for cases where
1.533 - * non-serializable objects are replaced by serializable ones.
1.534 - *
1.535 - * <p>When a subclass is replacing objects it must insure that either a
1.536 - * complementary substitution must be made during deserialization or that
1.537 - * the substituted object is compatible with every field where the
1.538 - * reference will be stored. Objects whose type is not a subclass of the
1.539 - * type of the field or array element abort the serialization by raising an
1.540 - * exception and the object is not be stored.
1.541 - *
1.542 - * <p>This method is called only once when each object is first
1.543 - * encountered. All subsequent references to the object will be redirected
1.544 - * to the new object. This method should return the object to be
1.545 - * substituted or the original object.
1.546 - *
1.547 - * <p>Null can be returned as the object to be substituted, but may cause
1.548 - * NullReferenceException in classes that contain references to the
1.549 - * original object since they may be expecting an object instead of
1.550 - * null.
1.551 - *
1.552 - * @param obj the object to be replaced
1.553 - * @return the alternate object that replaced the specified one
1.554 - * @throws IOException Any exception thrown by the underlying
1.555 - * OutputStream.
1.556 - */
1.557 - protected Object replaceObject(Object obj) throws IOException {
1.558 - return obj;
1.559 - }
1.560 -
1.561 - /**
1.562 - * Enable the stream to do replacement of objects in the stream. When
1.563 - * enabled, the replaceObject method is called for every object being
1.564 - * serialized.
1.565 - *
1.566 - * <p>If <code>enable</code> is true, and there is a security manager
1.567 - * installed, this method first calls the security manager's
1.568 - * <code>checkPermission</code> method with a
1.569 - * <code>SerializablePermission("enableSubstitution")</code> permission to
1.570 - * ensure it's ok to enable the stream to do replacement of objects in the
1.571 - * stream.
1.572 - *
1.573 - * @param enable boolean parameter to enable replacement of objects
1.574 - * @return the previous setting before this method was invoked
1.575 - * @throws SecurityException if a security manager exists and its
1.576 - * <code>checkPermission</code> method denies enabling the stream
1.577 - * to do replacement of objects in the stream.
1.578 - * @see SecurityManager#checkPermission
1.579 - * @see java.io.SerializablePermission
1.580 - */
1.581 - protected boolean enableReplaceObject(boolean enable)
1.582 - throws SecurityException
1.583 - {
1.584 - throw new SecurityException();
1.585 - }
1.586 -
1.587 - /**
1.588 - * The writeStreamHeader method is provided so subclasses can append or
1.589 - * prepend their own header to the stream. It writes the magic number and
1.590 - * version to the stream.
1.591 - *
1.592 - * @throws IOException if I/O errors occur while writing to the underlying
1.593 - * stream
1.594 - */
1.595 - protected void writeStreamHeader() throws IOException {
1.596 - bout.writeShort(STREAM_MAGIC);
1.597 - bout.writeShort(STREAM_VERSION);
1.598 - }
1.599 -
1.600 - /**
1.601 - * Write the specified class descriptor to the ObjectOutputStream. Class
1.602 - * descriptors are used to identify the classes of objects written to the
1.603 - * stream. Subclasses of ObjectOutputStream may override this method to
1.604 - * customize the way in which class descriptors are written to the
1.605 - * serialization stream. The corresponding method in ObjectInputStream,
1.606 - * <code>readClassDescriptor</code>, should then be overridden to
1.607 - * reconstitute the class descriptor from its custom stream representation.
1.608 - * By default, this method writes class descriptors according to the format
1.609 - * defined in the Object Serialization specification.
1.610 - *
1.611 - * <p>Note that this method will only be called if the ObjectOutputStream
1.612 - * is not using the old serialization stream format (set by calling
1.613 - * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
1.614 - * serialization stream is using the old format
1.615 - * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
1.616 - * internally in a manner that cannot be overridden or customized.
1.617 - *
1.618 - * @param desc class descriptor to write to the stream
1.619 - * @throws IOException If an I/O error has occurred.
1.620 - * @see java.io.ObjectInputStream#readClassDescriptor()
1.621 - * @see #useProtocolVersion(int)
1.622 - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
1.623 - * @since 1.3
1.624 - */
1.625 - protected void writeClassDescriptor(ObjectStreamClass desc)
1.626 - throws IOException
1.627 - {
1.628 - desc.writeNonProxy(this);
1.629 - }
1.630 -
1.631 - /**
1.632 - * Writes a byte. This method will block until the byte is actually
1.633 - * written.
1.634 - *
1.635 - * @param val the byte to be written to the stream
1.636 - * @throws IOException If an I/O error has occurred.
1.637 - */
1.638 - public void write(int val) throws IOException {
1.639 - bout.write(val);
1.640 - }
1.641 -
1.642 - /**
1.643 - * Writes an array of bytes. This method will block until the bytes are
1.644 - * actually written.
1.645 - *
1.646 - * @param buf the data to be written
1.647 - * @throws IOException If an I/O error has occurred.
1.648 - */
1.649 - public void write(byte[] buf) throws IOException {
1.650 - bout.write(buf, 0, buf.length, false);
1.651 - }
1.652 -
1.653 - /**
1.654 - * Writes a sub array of bytes.
1.655 - *
1.656 - * @param buf the data to be written
1.657 - * @param off the start offset in the data
1.658 - * @param len the number of bytes that are written
1.659 - * @throws IOException If an I/O error has occurred.
1.660 - */
1.661 - public void write(byte[] buf, int off, int len) throws IOException {
1.662 - if (buf == null) {
1.663 - throw new NullPointerException();
1.664 - }
1.665 - int endoff = off + len;
1.666 - if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
1.667 - throw new IndexOutOfBoundsException();
1.668 - }
1.669 - bout.write(buf, off, len, false);
1.670 - }
1.671 -
1.672 - /**
1.673 - * Flushes the stream. This will write any buffered output bytes and flush
1.674 - * through to the underlying stream.
1.675 - *
1.676 - * @throws IOException If an I/O error has occurred.
1.677 - */
1.678 - public void flush() throws IOException {
1.679 - bout.flush();
1.680 - }
1.681 -
1.682 - /**
1.683 - * Drain any buffered data in ObjectOutputStream. Similar to flush but
1.684 - * does not propagate the flush to the underlying stream.
1.685 - *
1.686 - * @throws IOException if I/O errors occur while writing to the underlying
1.687 - * stream
1.688 - */
1.689 - protected void drain() throws IOException {
1.690 - bout.drain();
1.691 - }
1.692 -
1.693 - /**
1.694 - * Closes the stream. This method must be called to release any resources
1.695 - * associated with the stream.
1.696 - *
1.697 - * @throws IOException If an I/O error has occurred.
1.698 - */
1.699 - public void close() throws IOException {
1.700 - flush();
1.701 - clear();
1.702 - bout.close();
1.703 - }
1.704 -
1.705 - /**
1.706 - * Writes a boolean.
1.707 - *
1.708 - * @param val the boolean to be written
1.709 - * @throws IOException if I/O errors occur while writing to the underlying
1.710 - * stream
1.711 - */
1.712 - public void writeBoolean(boolean val) throws IOException {
1.713 - bout.writeBoolean(val);
1.714 - }
1.715 -
1.716 - /**
1.717 - * Writes an 8 bit byte.
1.718 - *
1.719 - * @param val the byte value to be written
1.720 - * @throws IOException if I/O errors occur while writing to the underlying
1.721 - * stream
1.722 - */
1.723 - public void writeByte(int val) throws IOException {
1.724 - bout.writeByte(val);
1.725 - }
1.726 -
1.727 - /**
1.728 - * Writes a 16 bit short.
1.729 - *
1.730 - * @param val the short value to be written
1.731 - * @throws IOException if I/O errors occur while writing to the underlying
1.732 - * stream
1.733 - */
1.734 - public void writeShort(int val) throws IOException {
1.735 - bout.writeShort(val);
1.736 - }
1.737 -
1.738 - /**
1.739 - * Writes a 16 bit char.
1.740 - *
1.741 - * @param val the char value to be written
1.742 - * @throws IOException if I/O errors occur while writing to the underlying
1.743 - * stream
1.744 - */
1.745 - public void writeChar(int val) throws IOException {
1.746 - bout.writeChar(val);
1.747 - }
1.748 -
1.749 - /**
1.750 - * Writes a 32 bit int.
1.751 - *
1.752 - * @param val the integer value to be written
1.753 - * @throws IOException if I/O errors occur while writing to the underlying
1.754 - * stream
1.755 - */
1.756 - public void writeInt(int val) throws IOException {
1.757 - bout.writeInt(val);
1.758 - }
1.759 -
1.760 - /**
1.761 - * Writes a 64 bit long.
1.762 - *
1.763 - * @param val the long value to be written
1.764 - * @throws IOException if I/O errors occur while writing to the underlying
1.765 - * stream
1.766 - */
1.767 - public void writeLong(long val) throws IOException {
1.768 - bout.writeLong(val);
1.769 - }
1.770 -
1.771 - /**
1.772 - * Writes a 32 bit float.
1.773 - *
1.774 - * @param val the float value to be written
1.775 - * @throws IOException if I/O errors occur while writing to the underlying
1.776 - * stream
1.777 - */
1.778 - public void writeFloat(float val) throws IOException {
1.779 - bout.writeFloat(val);
1.780 - }
1.781 -
1.782 - /**
1.783 - * Writes a 64 bit double.
1.784 - *
1.785 - * @param val the double value to be written
1.786 - * @throws IOException if I/O errors occur while writing to the underlying
1.787 - * stream
1.788 - */
1.789 - public void writeDouble(double val) throws IOException {
1.790 - bout.writeDouble(val);
1.791 - }
1.792 -
1.793 - /**
1.794 - * Writes a String as a sequence of bytes.
1.795 - *
1.796 - * @param str the String of bytes to be written
1.797 - * @throws IOException if I/O errors occur while writing to the underlying
1.798 - * stream
1.799 - */
1.800 - public void writeBytes(String str) throws IOException {
1.801 - bout.writeBytes(str);
1.802 - }
1.803 -
1.804 - /**
1.805 - * Writes a String as a sequence of chars.
1.806 - *
1.807 - * @param str the String of chars to be written
1.808 - * @throws IOException if I/O errors occur while writing to the underlying
1.809 - * stream
1.810 - */
1.811 - public void writeChars(String str) throws IOException {
1.812 - bout.writeChars(str);
1.813 - }
1.814 -
1.815 - /**
1.816 - * Primitive data write of this String in
1.817 - * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
1.818 - * format. Note that there is a
1.819 - * significant difference between writing a String into the stream as
1.820 - * primitive data or as an Object. A String instance written by writeObject
1.821 - * is written into the stream as a String initially. Future writeObject()
1.822 - * calls write references to the string into the stream.
1.823 - *
1.824 - * @param str the String to be written
1.825 - * @throws IOException if I/O errors occur while writing to the underlying
1.826 - * stream
1.827 - */
1.828 - public void writeUTF(String str) throws IOException {
1.829 - bout.writeUTF(str);
1.830 - }
1.831 -
1.832 - /**
1.833 - * Provide programmatic access to the persistent fields to be written
1.834 - * to ObjectOutput.
1.835 - *
1.836 - * @since 1.2
1.837 - */
1.838 - public static abstract class PutField {
1.839 -
1.840 - /**
1.841 - * Put the value of the named boolean field into the persistent field.
1.842 - *
1.843 - * @param name the name of the serializable field
1.844 - * @param val the value to assign to the field
1.845 - * @throws IllegalArgumentException if <code>name</code> does not
1.846 - * match the name of a serializable field for the class whose fields
1.847 - * are being written, or if the type of the named field is not
1.848 - * <code>boolean</code>
1.849 - */
1.850 - public abstract void put(String name, boolean val);
1.851 -
1.852 - /**
1.853 - * Put the value of the named byte field into the persistent field.
1.854 - *
1.855 - * @param name the name of the serializable field
1.856 - * @param val the value to assign to the field
1.857 - * @throws IllegalArgumentException if <code>name</code> does not
1.858 - * match the name of a serializable field for the class whose fields
1.859 - * are being written, or if the type of the named field is not
1.860 - * <code>byte</code>
1.861 - */
1.862 - public abstract void put(String name, byte val);
1.863 -
1.864 - /**
1.865 - * Put the value of the named char field into the persistent field.
1.866 - *
1.867 - * @param name the name of the serializable field
1.868 - * @param val the value to assign to the field
1.869 - * @throws IllegalArgumentException if <code>name</code> does not
1.870 - * match the name of a serializable field for the class whose fields
1.871 - * are being written, or if the type of the named field is not
1.872 - * <code>char</code>
1.873 - */
1.874 - public abstract void put(String name, char val);
1.875 -
1.876 - /**
1.877 - * Put the value of the named short field into the persistent field.
1.878 - *
1.879 - * @param name the name of the serializable field
1.880 - * @param val the value to assign to the field
1.881 - * @throws IllegalArgumentException if <code>name</code> does not
1.882 - * match the name of a serializable field for the class whose fields
1.883 - * are being written, or if the type of the named field is not
1.884 - * <code>short</code>
1.885 - */
1.886 - public abstract void put(String name, short val);
1.887 -
1.888 - /**
1.889 - * Put the value of the named int field into the persistent field.
1.890 - *
1.891 - * @param name the name of the serializable field
1.892 - * @param val the value to assign to the field
1.893 - * @throws IllegalArgumentException if <code>name</code> does not
1.894 - * match the name of a serializable field for the class whose fields
1.895 - * are being written, or if the type of the named field is not
1.896 - * <code>int</code>
1.897 - */
1.898 - public abstract void put(String name, int val);
1.899 -
1.900 - /**
1.901 - * Put the value of the named long field into the persistent field.
1.902 - *
1.903 - * @param name the name of the serializable field
1.904 - * @param val the value to assign to the field
1.905 - * @throws IllegalArgumentException if <code>name</code> does not
1.906 - * match the name of a serializable field for the class whose fields
1.907 - * are being written, or if the type of the named field is not
1.908 - * <code>long</code>
1.909 - */
1.910 - public abstract void put(String name, long val);
1.911 -
1.912 - /**
1.913 - * Put the value of the named float field into the persistent field.
1.914 - *
1.915 - * @param name the name of the serializable field
1.916 - * @param val the value to assign to the field
1.917 - * @throws IllegalArgumentException if <code>name</code> does not
1.918 - * match the name of a serializable field for the class whose fields
1.919 - * are being written, or if the type of the named field is not
1.920 - * <code>float</code>
1.921 - */
1.922 - public abstract void put(String name, float val);
1.923 -
1.924 - /**
1.925 - * Put the value of the named double field into the persistent field.
1.926 - *
1.927 - * @param name the name of the serializable field
1.928 - * @param val the value to assign to the field
1.929 - * @throws IllegalArgumentException if <code>name</code> does not
1.930 - * match the name of a serializable field for the class whose fields
1.931 - * are being written, or if the type of the named field is not
1.932 - * <code>double</code>
1.933 - */
1.934 - public abstract void put(String name, double val);
1.935 -
1.936 - /**
1.937 - * Put the value of the named Object field into the persistent field.
1.938 - *
1.939 - * @param name the name of the serializable field
1.940 - * @param val the value to assign to the field
1.941 - * (which may be <code>null</code>)
1.942 - * @throws IllegalArgumentException if <code>name</code> does not
1.943 - * match the name of a serializable field for the class whose fields
1.944 - * are being written, or if the type of the named field is not a
1.945 - * reference type
1.946 - */
1.947 - public abstract void put(String name, Object val);
1.948 -
1.949 - /**
1.950 - * Write the data and fields to the specified ObjectOutput stream,
1.951 - * which must be the same stream that produced this
1.952 - * <code>PutField</code> object.
1.953 - *
1.954 - * @param out the stream to write the data and fields to
1.955 - * @throws IOException if I/O errors occur while writing to the
1.956 - * underlying stream
1.957 - * @throws IllegalArgumentException if the specified stream is not
1.958 - * the same stream that produced this <code>PutField</code>
1.959 - * object
1.960 - * @deprecated This method does not write the values contained by this
1.961 - * <code>PutField</code> object in a proper format, and may
1.962 - * result in corruption of the serialization stream. The
1.963 - * correct way to write <code>PutField</code> data is by
1.964 - * calling the {@link java.io.ObjectOutputStream#writeFields()}
1.965 - * method.
1.966 - */
1.967 - @Deprecated
1.968 - public abstract void write(ObjectOutput out) throws IOException;
1.969 - }
1.970 -
1.971 -
1.972 - /**
1.973 - * Returns protocol version in use.
1.974 - */
1.975 - int getProtocolVersion() {
1.976 - return protocol;
1.977 - }
1.978 -
1.979 - /**
1.980 - * Writes string without allowing it to be replaced in stream. Used by
1.981 - * ObjectStreamClass to write class descriptor type strings.
1.982 - */
1.983 - void writeTypeString(String str) throws IOException {
1.984 - int handle;
1.985 - if (str == null) {
1.986 - writeNull();
1.987 - } else if ((handle = handles.lookup(str)) != -1) {
1.988 - writeHandle(handle);
1.989 - } else {
1.990 - writeString(str, false);
1.991 - }
1.992 - }
1.993 -
1.994 - /**
1.995 - * Verifies that this (possibly subclass) instance can be constructed
1.996 - * without violating security constraints: the subclass must not override
1.997 - * security-sensitive non-final methods, or else the
1.998 - * "enableSubclassImplementation" SerializablePermission is checked.
1.999 - */
1.1000 - private void verifySubclass() {
1.1001 - Class cl = getClass();
1.1002 - if (cl == ObjectOutputStream.class) {
1.1003 - return;
1.1004 - }
1.1005 - throw new SecurityException();
1.1006 - }
1.1007 -
1.1008 - /**
1.1009 - * Clears internal data structures.
1.1010 - */
1.1011 - private void clear() {
1.1012 - subs.clear();
1.1013 - handles.clear();
1.1014 - }
1.1015 -
1.1016 - /**
1.1017 - * Underlying writeObject/writeUnshared implementation.
1.1018 - */
1.1019 - private void writeObject0(Object obj, boolean unshared)
1.1020 - throws IOException
1.1021 - {
1.1022 - boolean oldMode = bout.setBlockDataMode(false);
1.1023 - depth++;
1.1024 - try {
1.1025 - // handle previously written and non-replaceable objects
1.1026 - int h;
1.1027 - if ((obj = subs.lookup(obj)) == null) {
1.1028 - writeNull();
1.1029 - return;
1.1030 - } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1.1031 - writeHandle(h);
1.1032 - return;
1.1033 - } else if (obj instanceof Class) {
1.1034 - writeClass((Class) obj, unshared);
1.1035 - return;
1.1036 - } else if (obj instanceof ObjectStreamClass) {
1.1037 - writeClassDesc((ObjectStreamClass) obj, unshared);
1.1038 - return;
1.1039 - }
1.1040 -
1.1041 - // check for replacement object
1.1042 - Object orig = obj;
1.1043 - Class cl = obj.getClass();
1.1044 - ObjectStreamClass desc;
1.1045 - for (;;) {
1.1046 - // REMIND: skip this check for strings/arrays?
1.1047 - Class repCl;
1.1048 - desc = ObjectStreamClass.lookup(cl, true);
1.1049 - if (!desc.hasWriteReplaceMethod() ||
1.1050 - (obj = desc.invokeWriteReplace(obj)) == null ||
1.1051 - (repCl = obj.getClass()) == cl)
1.1052 - {
1.1053 - break;
1.1054 - }
1.1055 - cl = repCl;
1.1056 - }
1.1057 - if (enableReplace) {
1.1058 - Object rep = replaceObject(obj);
1.1059 - if (rep != obj && rep != null) {
1.1060 - cl = rep.getClass();
1.1061 - desc = ObjectStreamClass.lookup(cl, true);
1.1062 - }
1.1063 - obj = rep;
1.1064 - }
1.1065 -
1.1066 - // if object replaced, run through original checks a second time
1.1067 - if (obj != orig) {
1.1068 - subs.assign(orig, obj);
1.1069 - if (obj == null) {
1.1070 - writeNull();
1.1071 - return;
1.1072 - } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1.1073 - writeHandle(h);
1.1074 - return;
1.1075 - } else if (obj instanceof Class) {
1.1076 - writeClass((Class) obj, unshared);
1.1077 - return;
1.1078 - } else if (obj instanceof ObjectStreamClass) {
1.1079 - writeClassDesc((ObjectStreamClass) obj, unshared);
1.1080 - return;
1.1081 - }
1.1082 - }
1.1083 -
1.1084 - // remaining cases
1.1085 - if (obj instanceof String) {
1.1086 - writeString((String) obj, unshared);
1.1087 - } else if (cl.isArray()) {
1.1088 - writeArray(obj, desc, unshared);
1.1089 - } else if (obj instanceof Enum) {
1.1090 - writeEnum((Enum) obj, desc, unshared);
1.1091 - } else if (obj instanceof Serializable) {
1.1092 - writeOrdinaryObject(obj, desc, unshared);
1.1093 - } else {
1.1094 - if (extendedDebugInfo) {
1.1095 - throw new NotSerializableException(
1.1096 - cl.getName() + "\n" + debugInfoStack.toString());
1.1097 - } else {
1.1098 - throw new NotSerializableException(cl.getName());
1.1099 - }
1.1100 - }
1.1101 - } finally {
1.1102 - depth--;
1.1103 - bout.setBlockDataMode(oldMode);
1.1104 - }
1.1105 - }
1.1106 -
1.1107 - /**
1.1108 - * Writes null code to stream.
1.1109 - */
1.1110 - private void writeNull() throws IOException {
1.1111 - bout.writeByte(TC_NULL);
1.1112 - }
1.1113 -
1.1114 - /**
1.1115 - * Writes given object handle to stream.
1.1116 - */
1.1117 - private void writeHandle(int handle) throws IOException {
1.1118 - bout.writeByte(TC_REFERENCE);
1.1119 - bout.writeInt(baseWireHandle + handle);
1.1120 - }
1.1121 -
1.1122 - /**
1.1123 - * Writes representation of given class to stream.
1.1124 - */
1.1125 - private void writeClass(Class cl, boolean unshared) throws IOException {
1.1126 - bout.writeByte(TC_CLASS);
1.1127 - writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1.1128 - handles.assign(unshared ? null : cl);
1.1129 - }
1.1130 -
1.1131 - /**
1.1132 - * Writes representation of given class descriptor to stream.
1.1133 - */
1.1134 - private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1.1135 - throws IOException
1.1136 - {
1.1137 - int handle;
1.1138 - if (desc == null) {
1.1139 - writeNull();
1.1140 - } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1.1141 - writeHandle(handle);
1.1142 - } else if (desc.isProxy()) {
1.1143 - writeProxyDesc(desc, unshared);
1.1144 - } else {
1.1145 - writeNonProxyDesc(desc, unshared);
1.1146 - }
1.1147 - }
1.1148 -
1.1149 - /**
1.1150 - * Writes class descriptor representing a dynamic proxy class to stream.
1.1151 - */
1.1152 - private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1.1153 - throws IOException
1.1154 - {
1.1155 - bout.writeByte(TC_PROXYCLASSDESC);
1.1156 - handles.assign(unshared ? null : desc);
1.1157 -
1.1158 - Class cl = desc.forClass();
1.1159 - Class[] ifaces = cl.getInterfaces();
1.1160 - bout.writeInt(ifaces.length);
1.1161 - for (int i = 0; i < ifaces.length; i++) {
1.1162 - bout.writeUTF(ifaces[i].getName());
1.1163 - }
1.1164 -
1.1165 - bout.setBlockDataMode(true);
1.1166 - annotateProxyClass(cl);
1.1167 - bout.setBlockDataMode(false);
1.1168 - bout.writeByte(TC_ENDBLOCKDATA);
1.1169 -
1.1170 - writeClassDesc(desc.getSuperDesc(), false);
1.1171 - }
1.1172 -
1.1173 - /**
1.1174 - * Writes class descriptor representing a standard (i.e., not a dynamic
1.1175 - * proxy) class to stream.
1.1176 - */
1.1177 - private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
1.1178 - throws IOException
1.1179 - {
1.1180 - bout.writeByte(TC_CLASSDESC);
1.1181 - handles.assign(unshared ? null : desc);
1.1182 -
1.1183 - if (protocol == PROTOCOL_VERSION_1) {
1.1184 - // do not invoke class descriptor write hook with old protocol
1.1185 - desc.writeNonProxy(this);
1.1186 - } else {
1.1187 - writeClassDescriptor(desc);
1.1188 - }
1.1189 -
1.1190 - Class cl = desc.forClass();
1.1191 - bout.setBlockDataMode(true);
1.1192 - annotateClass(cl);
1.1193 - bout.setBlockDataMode(false);
1.1194 - bout.writeByte(TC_ENDBLOCKDATA);
1.1195 -
1.1196 - writeClassDesc(desc.getSuperDesc(), false);
1.1197 - }
1.1198 -
1.1199 - /**
1.1200 - * Writes given string to stream, using standard or long UTF format
1.1201 - * depending on string length.
1.1202 - */
1.1203 - private void writeString(String str, boolean unshared) throws IOException {
1.1204 - handles.assign(unshared ? null : str);
1.1205 - long utflen = bout.getUTFLength(str);
1.1206 - if (utflen <= 0xFFFF) {
1.1207 - bout.writeByte(TC_STRING);
1.1208 - bout.writeUTF(str, utflen);
1.1209 - } else {
1.1210 - bout.writeByte(TC_LONGSTRING);
1.1211 - bout.writeLongUTF(str, utflen);
1.1212 - }
1.1213 - }
1.1214 -
1.1215 - /**
1.1216 - * Writes given array object to stream.
1.1217 - */
1.1218 - private void writeArray(Object array,
1.1219 - ObjectStreamClass desc,
1.1220 - boolean unshared)
1.1221 - throws IOException
1.1222 - {
1.1223 - bout.writeByte(TC_ARRAY);
1.1224 - writeClassDesc(desc, false);
1.1225 - handles.assign(unshared ? null : array);
1.1226 -
1.1227 - Class ccl = desc.forClass().getComponentType();
1.1228 - if (ccl.isPrimitive()) {
1.1229 - if (ccl == Integer.TYPE) {
1.1230 - int[] ia = (int[]) array;
1.1231 - bout.writeInt(ia.length);
1.1232 - bout.writeInts(ia, 0, ia.length);
1.1233 - } else if (ccl == Byte.TYPE) {
1.1234 - byte[] ba = (byte[]) array;
1.1235 - bout.writeInt(ba.length);
1.1236 - bout.write(ba, 0, ba.length, true);
1.1237 - } else if (ccl == Long.TYPE) {
1.1238 - long[] ja = (long[]) array;
1.1239 - bout.writeInt(ja.length);
1.1240 - bout.writeLongs(ja, 0, ja.length);
1.1241 - } else if (ccl == Float.TYPE) {
1.1242 - float[] fa = (float[]) array;
1.1243 - bout.writeInt(fa.length);
1.1244 - bout.writeFloats(fa, 0, fa.length);
1.1245 - } else if (ccl == Double.TYPE) {
1.1246 - double[] da = (double[]) array;
1.1247 - bout.writeInt(da.length);
1.1248 - bout.writeDoubles(da, 0, da.length);
1.1249 - } else if (ccl == Short.TYPE) {
1.1250 - short[] sa = (short[]) array;
1.1251 - bout.writeInt(sa.length);
1.1252 - bout.writeShorts(sa, 0, sa.length);
1.1253 - } else if (ccl == Character.TYPE) {
1.1254 - char[] ca = (char[]) array;
1.1255 - bout.writeInt(ca.length);
1.1256 - bout.writeChars(ca, 0, ca.length);
1.1257 - } else if (ccl == Boolean.TYPE) {
1.1258 - boolean[] za = (boolean[]) array;
1.1259 - bout.writeInt(za.length);
1.1260 - bout.writeBooleans(za, 0, za.length);
1.1261 - } else {
1.1262 - throw new InternalError();
1.1263 - }
1.1264 - } else {
1.1265 - Object[] objs = (Object[]) array;
1.1266 - int len = objs.length;
1.1267 - bout.writeInt(len);
1.1268 - if (extendedDebugInfo) {
1.1269 - debugInfoStack.push(
1.1270 - "array (class \"" + array.getClass().getName() +
1.1271 - "\", size: " + len + ")");
1.1272 - }
1.1273 - try {
1.1274 - for (int i = 0; i < len; i++) {
1.1275 - if (extendedDebugInfo) {
1.1276 - debugInfoStack.push(
1.1277 - "element of array (index: " + i + ")");
1.1278 - }
1.1279 - try {
1.1280 - writeObject0(objs[i], false);
1.1281 - } finally {
1.1282 - if (extendedDebugInfo) {
1.1283 - debugInfoStack.pop();
1.1284 - }
1.1285 - }
1.1286 - }
1.1287 - } finally {
1.1288 - if (extendedDebugInfo) {
1.1289 - debugInfoStack.pop();
1.1290 - }
1.1291 - }
1.1292 - }
1.1293 - }
1.1294 -
1.1295 - /**
1.1296 - * Writes given enum constant to stream.
1.1297 - */
1.1298 - private void writeEnum(Enum en,
1.1299 - ObjectStreamClass desc,
1.1300 - boolean unshared)
1.1301 - throws IOException
1.1302 - {
1.1303 - bout.writeByte(TC_ENUM);
1.1304 - ObjectStreamClass sdesc = desc.getSuperDesc();
1.1305 - writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
1.1306 - handles.assign(unshared ? null : en);
1.1307 - writeString(en.name(), false);
1.1308 - }
1.1309 -
1.1310 - /**
1.1311 - * Writes representation of a "ordinary" (i.e., not a String, Class,
1.1312 - * ObjectStreamClass, array, or enum constant) serializable object to the
1.1313 - * stream.
1.1314 - */
1.1315 - private void writeOrdinaryObject(Object obj,
1.1316 - ObjectStreamClass desc,
1.1317 - boolean unshared)
1.1318 - throws IOException
1.1319 - {
1.1320 - if (extendedDebugInfo) {
1.1321 - debugInfoStack.push(
1.1322 - (depth == 1 ? "root " : "") + "object (class \"" +
1.1323 - obj.getClass().getName() + "\", " + obj.toString() + ")");
1.1324 - }
1.1325 - try {
1.1326 - desc.checkSerialize();
1.1327 -
1.1328 - bout.writeByte(TC_OBJECT);
1.1329 - writeClassDesc(desc, false);
1.1330 - handles.assign(unshared ? null : obj);
1.1331 - if (desc.isExternalizable() && !desc.isProxy()) {
1.1332 - writeExternalData((Externalizable) obj);
1.1333 - } else {
1.1334 - writeSerialData(obj, desc);
1.1335 - }
1.1336 - } finally {
1.1337 - if (extendedDebugInfo) {
1.1338 - debugInfoStack.pop();
1.1339 - }
1.1340 - }
1.1341 - }
1.1342 -
1.1343 - /**
1.1344 - * Writes externalizable data of given object by invoking its
1.1345 - * writeExternal() method.
1.1346 - */
1.1347 - private void writeExternalData(Externalizable obj) throws IOException {
1.1348 - PutFieldImpl oldPut = curPut;
1.1349 - curPut = null;
1.1350 -
1.1351 - if (extendedDebugInfo) {
1.1352 - debugInfoStack.push("writeExternal data");
1.1353 - }
1.1354 - Object oldContext = curContext;
1.1355 - try {
1.1356 - curContext = null;
1.1357 - if (protocol == PROTOCOL_VERSION_1) {
1.1358 - obj.writeExternal(this);
1.1359 - } else {
1.1360 - bout.setBlockDataMode(true);
1.1361 - obj.writeExternal(this);
1.1362 - bout.setBlockDataMode(false);
1.1363 - bout.writeByte(TC_ENDBLOCKDATA);
1.1364 - }
1.1365 - } finally {
1.1366 - curContext = oldContext;
1.1367 - if (extendedDebugInfo) {
1.1368 - debugInfoStack.pop();
1.1369 - }
1.1370 - }
1.1371 -
1.1372 - curPut = oldPut;
1.1373 - }
1.1374 -
1.1375 - /**
1.1376 - * Writes instance data for each serializable class of given object, from
1.1377 - * superclass to subclass.
1.1378 - */
1.1379 - private void writeSerialData(Object obj, ObjectStreamClass desc)
1.1380 - throws IOException
1.1381 - {
1.1382 - ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
1.1383 - for (int i = 0; i < slots.length; i++) {
1.1384 - ObjectStreamClass slotDesc = slots[i].desc;
1.1385 - if (slotDesc.hasWriteObjectMethod()) {
1.1386 - PutFieldImpl oldPut = curPut;
1.1387 - curPut = null;
1.1388 - Object oldContext = curContext;
1.1389 -
1.1390 - if (extendedDebugInfo) {
1.1391 - debugInfoStack.push(
1.1392 - "custom writeObject data (class \"" +
1.1393 - slotDesc.getName() + "\")");
1.1394 - }
1.1395 - try {
1.1396 - curContext = new Object(); //new SerialCallbackContext(obj, slotDesc);
1.1397 - bout.setBlockDataMode(true);
1.1398 - slotDesc.invokeWriteObject(obj, this);
1.1399 - bout.setBlockDataMode(false);
1.1400 - bout.writeByte(TC_ENDBLOCKDATA);
1.1401 - } finally {
1.1402 - //curContext.setUsed();
1.1403 - curContext = oldContext;
1.1404 - if (extendedDebugInfo) {
1.1405 - debugInfoStack.pop();
1.1406 - }
1.1407 - }
1.1408 -
1.1409 - curPut = oldPut;
1.1410 - } else {
1.1411 - defaultWriteFields(obj, slotDesc);
1.1412 - }
1.1413 - }
1.1414 - }
1.1415 -
1.1416 - /**
1.1417 - * Fetches and writes values of serializable fields of given object to
1.1418 - * stream. The given class descriptor specifies which field values to
1.1419 - * write, and in which order they should be written.
1.1420 - */
1.1421 - private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1.1422 - throws IOException
1.1423 - {
1.1424 - // REMIND: perform conservative isInstance check here?
1.1425 - desc.checkDefaultSerialize();
1.1426 -
1.1427 - int primDataSize = desc.getPrimDataSize();
1.1428 - if (primVals == null || primVals.length < primDataSize) {
1.1429 - primVals = new byte[primDataSize];
1.1430 - }
1.1431 - desc.getPrimFieldValues(obj, primVals);
1.1432 - bout.write(primVals, 0, primDataSize, false);
1.1433 -
1.1434 - ObjectStreamField[] fields = desc.getFields(false);
1.1435 - Object[] objVals = new Object[desc.getNumObjFields()];
1.1436 - int numPrimFields = fields.length - objVals.length;
1.1437 - desc.getObjFieldValues(obj, objVals);
1.1438 - for (int i = 0; i < objVals.length; i++) {
1.1439 - if (extendedDebugInfo) {
1.1440 - debugInfoStack.push(
1.1441 - "field (class \"" + desc.getName() + "\", name: \"" +
1.1442 - fields[numPrimFields + i].getName() + "\", type: \"" +
1.1443 - fields[numPrimFields + i].getType() + "\")");
1.1444 - }
1.1445 - try {
1.1446 - writeObject0(objVals[i],
1.1447 - fields[numPrimFields + i].isUnshared());
1.1448 - } finally {
1.1449 - if (extendedDebugInfo) {
1.1450 - debugInfoStack.pop();
1.1451 - }
1.1452 - }
1.1453 - }
1.1454 - }
1.1455 -
1.1456 - /**
1.1457 - * Attempts to write to stream fatal IOException that has caused
1.1458 - * serialization to abort.
1.1459 - */
1.1460 - private void writeFatalException(IOException ex) throws IOException {
1.1461 - /*
1.1462 - * Note: the serialization specification states that if a second
1.1463 - * IOException occurs while attempting to serialize the original fatal
1.1464 - * exception to the stream, then a StreamCorruptedException should be
1.1465 - * thrown (section 2.1). However, due to a bug in previous
1.1466 - * implementations of serialization, StreamCorruptedExceptions were
1.1467 - * rarely (if ever) actually thrown--the "root" exceptions from
1.1468 - * underlying streams were thrown instead. This historical behavior is
1.1469 - * followed here for consistency.
1.1470 - */
1.1471 - clear();
1.1472 - boolean oldMode = bout.setBlockDataMode(false);
1.1473 - try {
1.1474 - bout.writeByte(TC_EXCEPTION);
1.1475 - writeObject0(ex, false);
1.1476 - clear();
1.1477 - } finally {
1.1478 - bout.setBlockDataMode(oldMode);
1.1479 - }
1.1480 - }
1.1481 -
1.1482 - /**
1.1483 - * Converts specified span of float values into byte values.
1.1484 - */
1.1485 - // REMIND: remove once hotspot inlines Float.floatToIntBits
1.1486 - private static native void floatsToBytes(float[] src, int srcpos,
1.1487 - byte[] dst, int dstpos,
1.1488 - int nfloats);
1.1489 -
1.1490 - /**
1.1491 - * Converts specified span of double values into byte values.
1.1492 - */
1.1493 - // REMIND: remove once hotspot inlines Double.doubleToLongBits
1.1494 - private static native void doublesToBytes(double[] src, int srcpos,
1.1495 - byte[] dst, int dstpos,
1.1496 - int ndoubles);
1.1497 -
1.1498 - /**
1.1499 - * Default PutField implementation.
1.1500 - */
1.1501 - private class PutFieldImpl extends PutField {
1.1502 -
1.1503 - /** class descriptor describing serializable fields */
1.1504 - private final ObjectStreamClass desc;
1.1505 - /** primitive field values */
1.1506 - private final byte[] primVals;
1.1507 - /** object field values */
1.1508 - private final Object[] objVals;
1.1509 -
1.1510 - /**
1.1511 - * Creates PutFieldImpl object for writing fields defined in given
1.1512 - * class descriptor.
1.1513 - */
1.1514 - PutFieldImpl(ObjectStreamClass desc) {
1.1515 - this.desc = desc;
1.1516 - primVals = new byte[desc.getPrimDataSize()];
1.1517 - objVals = new Object[desc.getNumObjFields()];
1.1518 - }
1.1519 -
1.1520 - public void put(String name, boolean val) {
1.1521 - Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
1.1522 - }
1.1523 -
1.1524 - public void put(String name, byte val) {
1.1525 - primVals[getFieldOffset(name, Byte.TYPE)] = val;
1.1526 - }
1.1527 -
1.1528 - public void put(String name, char val) {
1.1529 - Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
1.1530 - }
1.1531 -
1.1532 - public void put(String name, short val) {
1.1533 - Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
1.1534 - }
1.1535 -
1.1536 - public void put(String name, int val) {
1.1537 - Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
1.1538 - }
1.1539 -
1.1540 - public void put(String name, float val) {
1.1541 - Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
1.1542 - }
1.1543 -
1.1544 - public void put(String name, long val) {
1.1545 - Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
1.1546 - }
1.1547 -
1.1548 - public void put(String name, double val) {
1.1549 - Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
1.1550 - }
1.1551 -
1.1552 - public void put(String name, Object val) {
1.1553 - objVals[getFieldOffset(name, Object.class)] = val;
1.1554 - }
1.1555 -
1.1556 - // deprecated in ObjectOutputStream.PutField
1.1557 - public void write(ObjectOutput out) throws IOException {
1.1558 - /*
1.1559 - * Applications should *not* use this method to write PutField
1.1560 - * data, as it will lead to stream corruption if the PutField
1.1561 - * object writes any primitive data (since block data mode is not
1.1562 - * unset/set properly, as is done in OOS.writeFields()). This
1.1563 - * broken implementation is being retained solely for behavioral
1.1564 - * compatibility, in order to support applications which use
1.1565 - * OOS.PutField.write() for writing only non-primitive data.
1.1566 - *
1.1567 - * Serialization of unshared objects is not implemented here since
1.1568 - * it is not necessary for backwards compatibility; also, unshared
1.1569 - * semantics may not be supported by the given ObjectOutput
1.1570 - * instance. Applications which write unshared objects using the
1.1571 - * PutField API must use OOS.writeFields().
1.1572 - */
1.1573 - if (ObjectOutputStream.this != out) {
1.1574 - throw new IllegalArgumentException("wrong stream");
1.1575 - }
1.1576 - out.write(primVals, 0, primVals.length);
1.1577 -
1.1578 - ObjectStreamField[] fields = desc.getFields(false);
1.1579 - int numPrimFields = fields.length - objVals.length;
1.1580 - // REMIND: warn if numPrimFields > 0?
1.1581 - for (int i = 0; i < objVals.length; i++) {
1.1582 - if (fields[numPrimFields + i].isUnshared()) {
1.1583 - throw new IOException("cannot write unshared object");
1.1584 - }
1.1585 - out.writeObject(objVals[i]);
1.1586 - }
1.1587 - }
1.1588 -
1.1589 - /**
1.1590 - * Writes buffered primitive data and object fields to stream.
1.1591 - */
1.1592 - void writeFields() throws IOException {
1.1593 - bout.write(primVals, 0, primVals.length, false);
1.1594 -
1.1595 - ObjectStreamField[] fields = desc.getFields(false);
1.1596 - int numPrimFields = fields.length - objVals.length;
1.1597 - for (int i = 0; i < objVals.length; i++) {
1.1598 - if (extendedDebugInfo) {
1.1599 - debugInfoStack.push(
1.1600 - "field (class \"" + desc.getName() + "\", name: \"" +
1.1601 - fields[numPrimFields + i].getName() + "\", type: \"" +
1.1602 - fields[numPrimFields + i].getType() + "\")");
1.1603 - }
1.1604 - try {
1.1605 - writeObject0(objVals[i],
1.1606 - fields[numPrimFields + i].isUnshared());
1.1607 - } finally {
1.1608 - if (extendedDebugInfo) {
1.1609 - debugInfoStack.pop();
1.1610 - }
1.1611 - }
1.1612 - }
1.1613 - }
1.1614 -
1.1615 - /**
1.1616 - * Returns offset of field with given name and type. A specified type
1.1617 - * of null matches all types, Object.class matches all non-primitive
1.1618 - * types, and any other non-null type matches assignable types only.
1.1619 - * Throws IllegalArgumentException if no matching field found.
1.1620 - */
1.1621 - private int getFieldOffset(String name, Class type) {
1.1622 - ObjectStreamField field = desc.getField(name, type);
1.1623 - if (field == null) {
1.1624 - throw new IllegalArgumentException("no such field " + name +
1.1625 - " with type " + type);
1.1626 - }
1.1627 - return field.getOffset();
1.1628 - }
1.1629 - }
1.1630 -
1.1631 - /**
1.1632 - * Buffered output stream with two modes: in default mode, outputs data in
1.1633 - * same format as DataOutputStream; in "block data" mode, outputs data
1.1634 - * bracketed by block data markers (see object serialization specification
1.1635 - * for details).
1.1636 - */
1.1637 - private static class BlockDataOutputStream
1.1638 - extends OutputStream implements DataOutput
1.1639 - {
1.1640 - /** maximum data block length */
1.1641 - private static final int MAX_BLOCK_SIZE = 1024;
1.1642 - /** maximum data block header length */
1.1643 - private static final int MAX_HEADER_SIZE = 5;
1.1644 - /** (tunable) length of char buffer (for writing strings) */
1.1645 - private static final int CHAR_BUF_SIZE = 256;
1.1646 -
1.1647 - /** buffer for writing general/block data */
1.1648 - private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1.1649 - /** buffer for writing block data headers */
1.1650 - private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1.1651 - /** char buffer for fast string writes */
1.1652 - private final char[] cbuf = new char[CHAR_BUF_SIZE];
1.1653 -
1.1654 - /** block data mode */
1.1655 - private boolean blkmode = false;
1.1656 - /** current offset into buf */
1.1657 - private int pos = 0;
1.1658 -
1.1659 - /** underlying output stream */
1.1660 - private final OutputStream out;
1.1661 - /** loopback stream (for data writes that span data blocks) */
1.1662 - private final DataOutputStream dout;
1.1663 -
1.1664 - /**
1.1665 - * Creates new BlockDataOutputStream on top of given underlying stream.
1.1666 - * Block data mode is turned off by default.
1.1667 - */
1.1668 - BlockDataOutputStream(OutputStream out) {
1.1669 - this.out = out;
1.1670 - dout = new DataOutputStream(this);
1.1671 - }
1.1672 -
1.1673 - /**
1.1674 - * Sets block data mode to the given mode (true == on, false == off)
1.1675 - * and returns the previous mode value. If the new mode is the same as
1.1676 - * the old mode, no action is taken. If the new mode differs from the
1.1677 - * old mode, any buffered data is flushed before switching to the new
1.1678 - * mode.
1.1679 - */
1.1680 - boolean setBlockDataMode(boolean mode) throws IOException {
1.1681 - if (blkmode == mode) {
1.1682 - return blkmode;
1.1683 - }
1.1684 - drain();
1.1685 - blkmode = mode;
1.1686 - return !blkmode;
1.1687 - }
1.1688 -
1.1689 - /**
1.1690 - * Returns true if the stream is currently in block data mode, false
1.1691 - * otherwise.
1.1692 - */
1.1693 - boolean getBlockDataMode() {
1.1694 - return blkmode;
1.1695 - }
1.1696 -
1.1697 - /* ----------------- generic output stream methods ----------------- */
1.1698 - /*
1.1699 - * The following methods are equivalent to their counterparts in
1.1700 - * OutputStream, except that they partition written data into data
1.1701 - * blocks when in block data mode.
1.1702 - */
1.1703 -
1.1704 - public void write(int b) throws IOException {
1.1705 - if (pos >= MAX_BLOCK_SIZE) {
1.1706 - drain();
1.1707 - }
1.1708 - buf[pos++] = (byte) b;
1.1709 - }
1.1710 -
1.1711 - public void write(byte[] b) throws IOException {
1.1712 - write(b, 0, b.length, false);
1.1713 - }
1.1714 -
1.1715 - public void write(byte[] b, int off, int len) throws IOException {
1.1716 - write(b, off, len, false);
1.1717 - }
1.1718 -
1.1719 - public void flush() throws IOException {
1.1720 - drain();
1.1721 - out.flush();
1.1722 - }
1.1723 -
1.1724 - public void close() throws IOException {
1.1725 - flush();
1.1726 - out.close();
1.1727 - }
1.1728 -
1.1729 - /**
1.1730 - * Writes specified span of byte values from given array. If copy is
1.1731 - * true, copies the values to an intermediate buffer before writing
1.1732 - * them to underlying stream (to avoid exposing a reference to the
1.1733 - * original byte array).
1.1734 - */
1.1735 - void write(byte[] b, int off, int len, boolean copy)
1.1736 - throws IOException
1.1737 - {
1.1738 - if (!(copy || blkmode)) { // write directly
1.1739 - drain();
1.1740 - out.write(b, off, len);
1.1741 - return;
1.1742 - }
1.1743 -
1.1744 - while (len > 0) {
1.1745 - if (pos >= MAX_BLOCK_SIZE) {
1.1746 - drain();
1.1747 - }
1.1748 - if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1.1749 - // avoid unnecessary copy
1.1750 - writeBlockHeader(MAX_BLOCK_SIZE);
1.1751 - out.write(b, off, MAX_BLOCK_SIZE);
1.1752 - off += MAX_BLOCK_SIZE;
1.1753 - len -= MAX_BLOCK_SIZE;
1.1754 - } else {
1.1755 - int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1.1756 - System.arraycopy(b, off, buf, pos, wlen);
1.1757 - pos += wlen;
1.1758 - off += wlen;
1.1759 - len -= wlen;
1.1760 - }
1.1761 - }
1.1762 - }
1.1763 -
1.1764 - /**
1.1765 - * Writes all buffered data from this stream to the underlying stream,
1.1766 - * but does not flush underlying stream.
1.1767 - */
1.1768 - void drain() throws IOException {
1.1769 - if (pos == 0) {
1.1770 - return;
1.1771 - }
1.1772 - if (blkmode) {
1.1773 - writeBlockHeader(pos);
1.1774 - }
1.1775 - out.write(buf, 0, pos);
1.1776 - pos = 0;
1.1777 - }
1.1778 -
1.1779 - /**
1.1780 - * Writes block data header. Data blocks shorter than 256 bytes are
1.1781 - * prefixed with a 2-byte header; all others start with a 5-byte
1.1782 - * header.
1.1783 - */
1.1784 - private void writeBlockHeader(int len) throws IOException {
1.1785 - if (len <= 0xFF) {
1.1786 - hbuf[0] = TC_BLOCKDATA;
1.1787 - hbuf[1] = (byte) len;
1.1788 - out.write(hbuf, 0, 2);
1.1789 - } else {
1.1790 - hbuf[0] = TC_BLOCKDATALONG;
1.1791 - Bits.putInt(hbuf, 1, len);
1.1792 - out.write(hbuf, 0, 5);
1.1793 - }
1.1794 - }
1.1795 -
1.1796 -
1.1797 - /* ----------------- primitive data output methods ----------------- */
1.1798 - /*
1.1799 - * The following methods are equivalent to their counterparts in
1.1800 - * DataOutputStream, except that they partition written data into data
1.1801 - * blocks when in block data mode.
1.1802 - */
1.1803 -
1.1804 - public void writeBoolean(boolean v) throws IOException {
1.1805 - if (pos >= MAX_BLOCK_SIZE) {
1.1806 - drain();
1.1807 - }
1.1808 - Bits.putBoolean(buf, pos++, v);
1.1809 - }
1.1810 -
1.1811 - public void writeByte(int v) throws IOException {
1.1812 - if (pos >= MAX_BLOCK_SIZE) {
1.1813 - drain();
1.1814 - }
1.1815 - buf[pos++] = (byte) v;
1.1816 - }
1.1817 -
1.1818 - public void writeChar(int v) throws IOException {
1.1819 - if (pos + 2 <= MAX_BLOCK_SIZE) {
1.1820 - Bits.putChar(buf, pos, (char) v);
1.1821 - pos += 2;
1.1822 - } else {
1.1823 - dout.writeChar(v);
1.1824 - }
1.1825 - }
1.1826 -
1.1827 - public void writeShort(int v) throws IOException {
1.1828 - if (pos + 2 <= MAX_BLOCK_SIZE) {
1.1829 - Bits.putShort(buf, pos, (short) v);
1.1830 - pos += 2;
1.1831 - } else {
1.1832 - dout.writeShort(v);
1.1833 - }
1.1834 - }
1.1835 -
1.1836 - public void writeInt(int v) throws IOException {
1.1837 - if (pos + 4 <= MAX_BLOCK_SIZE) {
1.1838 - Bits.putInt(buf, pos, v);
1.1839 - pos += 4;
1.1840 - } else {
1.1841 - dout.writeInt(v);
1.1842 - }
1.1843 - }
1.1844 -
1.1845 - public void writeFloat(float v) throws IOException {
1.1846 - if (pos + 4 <= MAX_BLOCK_SIZE) {
1.1847 - Bits.putFloat(buf, pos, v);
1.1848 - pos += 4;
1.1849 - } else {
1.1850 - dout.writeFloat(v);
1.1851 - }
1.1852 - }
1.1853 -
1.1854 - public void writeLong(long v) throws IOException {
1.1855 - if (pos + 8 <= MAX_BLOCK_SIZE) {
1.1856 - Bits.putLong(buf, pos, v);
1.1857 - pos += 8;
1.1858 - } else {
1.1859 - dout.writeLong(v);
1.1860 - }
1.1861 - }
1.1862 -
1.1863 - public void writeDouble(double v) throws IOException {
1.1864 - if (pos + 8 <= MAX_BLOCK_SIZE) {
1.1865 - Bits.putDouble(buf, pos, v);
1.1866 - pos += 8;
1.1867 - } else {
1.1868 - dout.writeDouble(v);
1.1869 - }
1.1870 - }
1.1871 -
1.1872 - public void writeBytes(String s) throws IOException {
1.1873 - int endoff = s.length();
1.1874 - int cpos = 0;
1.1875 - int csize = 0;
1.1876 - for (int off = 0; off < endoff; ) {
1.1877 - if (cpos >= csize) {
1.1878 - cpos = 0;
1.1879 - csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1.1880 - s.getChars(off, off + csize, cbuf, 0);
1.1881 - }
1.1882 - if (pos >= MAX_BLOCK_SIZE) {
1.1883 - drain();
1.1884 - }
1.1885 - int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1.1886 - int stop = pos + n;
1.1887 - while (pos < stop) {
1.1888 - buf[pos++] = (byte) cbuf[cpos++];
1.1889 - }
1.1890 - off += n;
1.1891 - }
1.1892 - }
1.1893 -
1.1894 - public void writeChars(String s) throws IOException {
1.1895 - int endoff = s.length();
1.1896 - for (int off = 0; off < endoff; ) {
1.1897 - int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1.1898 - s.getChars(off, off + csize, cbuf, 0);
1.1899 - writeChars(cbuf, 0, csize);
1.1900 - off += csize;
1.1901 - }
1.1902 - }
1.1903 -
1.1904 - public void writeUTF(String s) throws IOException {
1.1905 - writeUTF(s, getUTFLength(s));
1.1906 - }
1.1907 -
1.1908 -
1.1909 - /* -------------- primitive data array output methods -------------- */
1.1910 - /*
1.1911 - * The following methods write out spans of primitive data values.
1.1912 - * Though equivalent to calling the corresponding primitive write
1.1913 - * methods repeatedly, these methods are optimized for writing groups
1.1914 - * of primitive data values more efficiently.
1.1915 - */
1.1916 -
1.1917 - void writeBooleans(boolean[] v, int off, int len) throws IOException {
1.1918 - int endoff = off + len;
1.1919 - while (off < endoff) {
1.1920 - if (pos >= MAX_BLOCK_SIZE) {
1.1921 - drain();
1.1922 - }
1.1923 - int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
1.1924 - while (off < stop) {
1.1925 - Bits.putBoolean(buf, pos++, v[off++]);
1.1926 - }
1.1927 - }
1.1928 - }
1.1929 -
1.1930 - void writeChars(char[] v, int off, int len) throws IOException {
1.1931 - int limit = MAX_BLOCK_SIZE - 2;
1.1932 - int endoff = off + len;
1.1933 - while (off < endoff) {
1.1934 - if (pos <= limit) {
1.1935 - int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1.1936 - int stop = Math.min(endoff, off + avail);
1.1937 - while (off < stop) {
1.1938 - Bits.putChar(buf, pos, v[off++]);
1.1939 - pos += 2;
1.1940 - }
1.1941 - } else {
1.1942 - dout.writeChar(v[off++]);
1.1943 - }
1.1944 - }
1.1945 - }
1.1946 -
1.1947 - void writeShorts(short[] v, int off, int len) throws IOException {
1.1948 - int limit = MAX_BLOCK_SIZE - 2;
1.1949 - int endoff = off + len;
1.1950 - while (off < endoff) {
1.1951 - if (pos <= limit) {
1.1952 - int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1.1953 - int stop = Math.min(endoff, off + avail);
1.1954 - while (off < stop) {
1.1955 - Bits.putShort(buf, pos, v[off++]);
1.1956 - pos += 2;
1.1957 - }
1.1958 - } else {
1.1959 - dout.writeShort(v[off++]);
1.1960 - }
1.1961 - }
1.1962 - }
1.1963 -
1.1964 - void writeInts(int[] v, int off, int len) throws IOException {
1.1965 - int limit = MAX_BLOCK_SIZE - 4;
1.1966 - int endoff = off + len;
1.1967 - while (off < endoff) {
1.1968 - if (pos <= limit) {
1.1969 - int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1.1970 - int stop = Math.min(endoff, off + avail);
1.1971 - while (off < stop) {
1.1972 - Bits.putInt(buf, pos, v[off++]);
1.1973 - pos += 4;
1.1974 - }
1.1975 - } else {
1.1976 - dout.writeInt(v[off++]);
1.1977 - }
1.1978 - }
1.1979 - }
1.1980 -
1.1981 - void writeFloats(float[] v, int off, int len) throws IOException {
1.1982 - int limit = MAX_BLOCK_SIZE - 4;
1.1983 - int endoff = off + len;
1.1984 - while (off < endoff) {
1.1985 - if (pos <= limit) {
1.1986 - int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1.1987 - int chunklen = Math.min(endoff - off, avail);
1.1988 - floatsToBytes(v, off, buf, pos, chunklen);
1.1989 - off += chunklen;
1.1990 - pos += chunklen << 2;
1.1991 - } else {
1.1992 - dout.writeFloat(v[off++]);
1.1993 - }
1.1994 - }
1.1995 - }
1.1996 -
1.1997 - void writeLongs(long[] v, int off, int len) throws IOException {
1.1998 - int limit = MAX_BLOCK_SIZE - 8;
1.1999 - int endoff = off + len;
1.2000 - while (off < endoff) {
1.2001 - if (pos <= limit) {
1.2002 - int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1.2003 - int stop = Math.min(endoff, off + avail);
1.2004 - while (off < stop) {
1.2005 - Bits.putLong(buf, pos, v[off++]);
1.2006 - pos += 8;
1.2007 - }
1.2008 - } else {
1.2009 - dout.writeLong(v[off++]);
1.2010 - }
1.2011 - }
1.2012 - }
1.2013 -
1.2014 - void writeDoubles(double[] v, int off, int len) throws IOException {
1.2015 - int limit = MAX_BLOCK_SIZE - 8;
1.2016 - int endoff = off + len;
1.2017 - while (off < endoff) {
1.2018 - if (pos <= limit) {
1.2019 - int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1.2020 - int chunklen = Math.min(endoff - off, avail);
1.2021 - doublesToBytes(v, off, buf, pos, chunklen);
1.2022 - off += chunklen;
1.2023 - pos += chunklen << 3;
1.2024 - } else {
1.2025 - dout.writeDouble(v[off++]);
1.2026 - }
1.2027 - }
1.2028 - }
1.2029 -
1.2030 - /**
1.2031 - * Returns the length in bytes of the UTF encoding of the given string.
1.2032 - */
1.2033 - long getUTFLength(String s) {
1.2034 - int len = s.length();
1.2035 - long utflen = 0;
1.2036 - for (int off = 0; off < len; ) {
1.2037 - int csize = Math.min(len - off, CHAR_BUF_SIZE);
1.2038 - s.getChars(off, off + csize, cbuf, 0);
1.2039 - for (int cpos = 0; cpos < csize; cpos++) {
1.2040 - char c = cbuf[cpos];
1.2041 - if (c >= 0x0001 && c <= 0x007F) {
1.2042 - utflen++;
1.2043 - } else if (c > 0x07FF) {
1.2044 - utflen += 3;
1.2045 - } else {
1.2046 - utflen += 2;
1.2047 - }
1.2048 - }
1.2049 - off += csize;
1.2050 - }
1.2051 - return utflen;
1.2052 - }
1.2053 -
1.2054 - /**
1.2055 - * Writes the given string in UTF format. This method is used in
1.2056 - * situations where the UTF encoding length of the string is already
1.2057 - * known; specifying it explicitly avoids a prescan of the string to
1.2058 - * determine its UTF length.
1.2059 - */
1.2060 - void writeUTF(String s, long utflen) throws IOException {
1.2061 - if (utflen > 0xFFFFL) {
1.2062 - throw new UTFDataFormatException();
1.2063 - }
1.2064 - writeShort((int) utflen);
1.2065 - if (utflen == (long) s.length()) {
1.2066 - writeBytes(s);
1.2067 - } else {
1.2068 - writeUTFBody(s);
1.2069 - }
1.2070 - }
1.2071 -
1.2072 - /**
1.2073 - * Writes given string in "long" UTF format. "Long" UTF format is
1.2074 - * identical to standard UTF, except that it uses an 8 byte header
1.2075 - * (instead of the standard 2 bytes) to convey the UTF encoding length.
1.2076 - */
1.2077 - void writeLongUTF(String s) throws IOException {
1.2078 - writeLongUTF(s, getUTFLength(s));
1.2079 - }
1.2080 -
1.2081 - /**
1.2082 - * Writes given string in "long" UTF format, where the UTF encoding
1.2083 - * length of the string is already known.
1.2084 - */
1.2085 - void writeLongUTF(String s, long utflen) throws IOException {
1.2086 - writeLong(utflen);
1.2087 - if (utflen == (long) s.length()) {
1.2088 - writeBytes(s);
1.2089 - } else {
1.2090 - writeUTFBody(s);
1.2091 - }
1.2092 - }
1.2093 -
1.2094 - /**
1.2095 - * Writes the "body" (i.e., the UTF representation minus the 2-byte or
1.2096 - * 8-byte length header) of the UTF encoding for the given string.
1.2097 - */
1.2098 - private void writeUTFBody(String s) throws IOException {
1.2099 - int limit = MAX_BLOCK_SIZE - 3;
1.2100 - int len = s.length();
1.2101 - for (int off = 0; off < len; ) {
1.2102 - int csize = Math.min(len - off, CHAR_BUF_SIZE);
1.2103 - s.getChars(off, off + csize, cbuf, 0);
1.2104 - for (int cpos = 0; cpos < csize; cpos++) {
1.2105 - char c = cbuf[cpos];
1.2106 - if (pos <= limit) {
1.2107 - if (c <= 0x007F && c != 0) {
1.2108 - buf[pos++] = (byte) c;
1.2109 - } else if (c > 0x07FF) {
1.2110 - buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
1.2111 - buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
1.2112 - buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
1.2113 - pos += 3;
1.2114 - } else {
1.2115 - buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
1.2116 - buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
1.2117 - pos += 2;
1.2118 - }
1.2119 - } else { // write one byte at a time to normalize block
1.2120 - if (c <= 0x007F && c != 0) {
1.2121 - write(c);
1.2122 - } else if (c > 0x07FF) {
1.2123 - write(0xE0 | ((c >> 12) & 0x0F));
1.2124 - write(0x80 | ((c >> 6) & 0x3F));
1.2125 - write(0x80 | ((c >> 0) & 0x3F));
1.2126 - } else {
1.2127 - write(0xC0 | ((c >> 6) & 0x1F));
1.2128 - write(0x80 | ((c >> 0) & 0x3F));
1.2129 - }
1.2130 - }
1.2131 - }
1.2132 - off += csize;
1.2133 - }
1.2134 - }
1.2135 - }
1.2136 -
1.2137 - /**
1.2138 - * Lightweight identity hash table which maps objects to integer handles,
1.2139 - * assigned in ascending order.
1.2140 - */
1.2141 - private static class HandleTable {
1.2142 -
1.2143 - /* number of mappings in table/next available handle */
1.2144 - private int size;
1.2145 - /* size threshold determining when to expand hash spine */
1.2146 - private int threshold;
1.2147 - /* factor for computing size threshold */
1.2148 - private final float loadFactor;
1.2149 - /* maps hash value -> candidate handle value */
1.2150 - private int[] spine;
1.2151 - /* maps handle value -> next candidate handle value */
1.2152 - private int[] next;
1.2153 - /* maps handle value -> associated object */
1.2154 - private Object[] objs;
1.2155 -
1.2156 - /**
1.2157 - * Creates new HandleTable with given capacity and load factor.
1.2158 - */
1.2159 - HandleTable(int initialCapacity, float loadFactor) {
1.2160 - this.loadFactor = loadFactor;
1.2161 - spine = new int[initialCapacity];
1.2162 - next = new int[initialCapacity];
1.2163 - objs = new Object[initialCapacity];
1.2164 - threshold = (int) (initialCapacity * loadFactor);
1.2165 - clear();
1.2166 - }
1.2167 -
1.2168 - /**
1.2169 - * Assigns next available handle to given object, and returns handle
1.2170 - * value. Handles are assigned in ascending order starting at 0.
1.2171 - */
1.2172 - int assign(Object obj) {
1.2173 - if (size >= next.length) {
1.2174 - growEntries();
1.2175 - }
1.2176 - if (size >= threshold) {
1.2177 - growSpine();
1.2178 - }
1.2179 - insert(obj, size);
1.2180 - return size++;
1.2181 - }
1.2182 -
1.2183 - /**
1.2184 - * Looks up and returns handle associated with given object, or -1 if
1.2185 - * no mapping found.
1.2186 - */
1.2187 - int lookup(Object obj) {
1.2188 - if (size == 0) {
1.2189 - return -1;
1.2190 - }
1.2191 - int index = hash(obj) % spine.length;
1.2192 - for (int i = spine[index]; i >= 0; i = next[i]) {
1.2193 - if (objs[i] == obj) {
1.2194 - return i;
1.2195 - }
1.2196 - }
1.2197 - return -1;
1.2198 - }
1.2199 -
1.2200 - /**
1.2201 - * Resets table to its initial (empty) state.
1.2202 - */
1.2203 - void clear() {
1.2204 - Arrays.fill(spine, -1);
1.2205 - Arrays.fill(objs, 0, size, null);
1.2206 - size = 0;
1.2207 - }
1.2208 -
1.2209 - /**
1.2210 - * Returns the number of mappings currently in table.
1.2211 - */
1.2212 - int size() {
1.2213 - return size;
1.2214 - }
1.2215 -
1.2216 - /**
1.2217 - * Inserts mapping object -> handle mapping into table. Assumes table
1.2218 - * is large enough to accommodate new mapping.
1.2219 - */
1.2220 - private void insert(Object obj, int handle) {
1.2221 - int index = hash(obj) % spine.length;
1.2222 - objs[handle] = obj;
1.2223 - next[handle] = spine[index];
1.2224 - spine[index] = handle;
1.2225 - }
1.2226 -
1.2227 - /**
1.2228 - * Expands the hash "spine" -- equivalent to increasing the number of
1.2229 - * buckets in a conventional hash table.
1.2230 - */
1.2231 - private void growSpine() {
1.2232 - spine = new int[(spine.length << 1) + 1];
1.2233 - threshold = (int) (spine.length * loadFactor);
1.2234 - Arrays.fill(spine, -1);
1.2235 - for (int i = 0; i < size; i++) {
1.2236 - insert(objs[i], i);
1.2237 - }
1.2238 - }
1.2239 -
1.2240 - /**
1.2241 - * Increases hash table capacity by lengthening entry arrays.
1.2242 - */
1.2243 - private void growEntries() {
1.2244 - int newLength = (next.length << 1) + 1;
1.2245 - int[] newNext = new int[newLength];
1.2246 - System.arraycopy(next, 0, newNext, 0, size);
1.2247 - next = newNext;
1.2248 -
1.2249 - Object[] newObjs = new Object[newLength];
1.2250 - System.arraycopy(objs, 0, newObjs, 0, size);
1.2251 - objs = newObjs;
1.2252 - }
1.2253 -
1.2254 - /**
1.2255 - * Returns hash value for given object.
1.2256 - */
1.2257 - private int hash(Object obj) {
1.2258 - return System.identityHashCode(obj) & 0x7FFFFFFF;
1.2259 - }
1.2260 - }
1.2261 -
1.2262 - /**
1.2263 - * Lightweight identity hash table which maps objects to replacement
1.2264 - * objects.
1.2265 - */
1.2266 - private static class ReplaceTable {
1.2267 -
1.2268 - /* maps object -> index */
1.2269 - private final HandleTable htab;
1.2270 - /* maps index -> replacement object */
1.2271 - private Object[] reps;
1.2272 -
1.2273 - /**
1.2274 - * Creates new ReplaceTable with given capacity and load factor.
1.2275 - */
1.2276 - ReplaceTable(int initialCapacity, float loadFactor) {
1.2277 - htab = new HandleTable(initialCapacity, loadFactor);
1.2278 - reps = new Object[initialCapacity];
1.2279 - }
1.2280 -
1.2281 - /**
1.2282 - * Enters mapping from object to replacement object.
1.2283 - */
1.2284 - void assign(Object obj, Object rep) {
1.2285 - int index = htab.assign(obj);
1.2286 - while (index >= reps.length) {
1.2287 - grow();
1.2288 - }
1.2289 - reps[index] = rep;
1.2290 - }
1.2291 -
1.2292 - /**
1.2293 - * Looks up and returns replacement for given object. If no
1.2294 - * replacement is found, returns the lookup object itself.
1.2295 - */
1.2296 - Object lookup(Object obj) {
1.2297 - int index = htab.lookup(obj);
1.2298 - return (index >= 0) ? reps[index] : obj;
1.2299 - }
1.2300 -
1.2301 - /**
1.2302 - * Resets table to its initial (empty) state.
1.2303 - */
1.2304 - void clear() {
1.2305 - Arrays.fill(reps, 0, htab.size(), null);
1.2306 - htab.clear();
1.2307 - }
1.2308 -
1.2309 - /**
1.2310 - * Returns the number of mappings currently in table.
1.2311 - */
1.2312 - int size() {
1.2313 - return htab.size();
1.2314 - }
1.2315 -
1.2316 - /**
1.2317 - * Increases table capacity.
1.2318 - */
1.2319 - private void grow() {
1.2320 - Object[] newReps = new Object[(reps.length << 1) + 1];
1.2321 - System.arraycopy(reps, 0, newReps, 0, reps.length);
1.2322 - reps = newReps;
1.2323 - }
1.2324 - }
1.2325 -
1.2326 - /**
1.2327 - * Stack to keep debug information about the state of the
1.2328 - * serialization process, for embedding in exception messages.
1.2329 - */
1.2330 - private static class DebugTraceInfoStack {
1.2331 - private final List<String> stack;
1.2332 -
1.2333 - DebugTraceInfoStack() {
1.2334 - stack = new ArrayList<>();
1.2335 - }
1.2336 -
1.2337 - /**
1.2338 - * Removes all of the elements from enclosed list.
1.2339 - */
1.2340 - void clear() {
1.2341 - stack.clear();
1.2342 - }
1.2343 -
1.2344 - /**
1.2345 - * Removes the object at the top of enclosed list.
1.2346 - */
1.2347 - void pop() {
1.2348 - stack.remove(stack.size()-1);
1.2349 - }
1.2350 -
1.2351 - /**
1.2352 - * Pushes a String onto the top of enclosed list.
1.2353 - */
1.2354 - void push(String entry) {
1.2355 - stack.add("\t- " + entry);
1.2356 - }
1.2357 -
1.2358 - /**
1.2359 - * Returns a string representation of this object
1.2360 - */
1.2361 - public String toString() {
1.2362 - StringBuilder buffer = new StringBuilder();
1.2363 - if (!stack.isEmpty()) {
1.2364 - for(int i = stack.size(); i > 0; i-- ) {
1.2365 - buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
1.2366 - }
1.2367 - }
1.2368 - return buffer.toString();
1.2369 - }
1.2370 - }
1.2371 -
1.2372 -}