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