0001: /*
0002: * @(#)ObjectOutputStream.java 1.112 02/01/03
0003: *
0004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: *
0026: */
0027:
0028: package java.io;
0029:
0030: import java.security.AccessController;
0031: import java.security.PrivilegedAction;
0032: import java.util.Arrays;
0033: import sun.misc.SoftCache;
0034: import sun.misc.CVM;
0035:
0036: /**
0037: * An ObjectOutputStream writes primitive data types and graphs of Java objects
0038: * to an OutputStream. The objects can be read (reconstituted) using an
0039: * ObjectInputStream. Persistent storage of objects can be accomplished by
0040: * using a file for the stream. If the stream is a network socket stream, the
0041: * objects can be reconsituted on another host or in another process.
0042: *
0043: * <p>Only objects that support the java.io.Serializable interface can be
0044: * written to streams. The class of each serializable object is encoded
0045: * including the class name and signature of the class, the values of the
0046: * object's fields and arrays, and the closure of any other objects referenced
0047: * from the initial objects.
0048: *
0049: * <p>The method writeObject is used to write an object to the stream. Any
0050: * object, including Strings and arrays, is written with writeObject. Multiple
0051: * objects or primitives can be written to the stream. The objects must be
0052: * read back from the corresponding ObjectInputstream with the same types and
0053: * in the same order as they were written.
0054: *
0055: * <p>Primitive data types can also be written to the stream using the
0056: * appropriate methods from DataOutput. Strings can also be written using the
0057: * writeUTF method.
0058: *
0059: * <p>The default serialization mechanism for an object writes the class of the
0060: * object, the class signature, and the values of all non-transient and
0061: * non-static fields. References to other objects (except in transient or
0062: * static fields) cause those objects to be written also. Multiple references
0063: * to a single object are encoded using a reference sharing mechanism so that
0064: * graphs of objects can be restored to the same shape as when the original was
0065: * written.
0066: *
0067: * <p>For example to write an object that can be read by the example in
0068: * ObjectInputStream:
0069: * <br>
0070: * <pre>
0071: * FileOutputStream fos = new FileOutputStream("t.tmp");
0072: * ObjectOutputStream oos = new ObjectOutputStream(fos);
0073: *
0074: * oos.writeInt(12345);
0075: * oos.writeObject("Today");
0076: * oos.writeObject(new Date());
0077: *
0078: * oos.close();
0079: * </pre>
0080: *
0081: * <p>Classes that require special handling during the serialization and
0082: * deserialization process must implement special methods with these exact
0083: * signatures:
0084: * <br>
0085: * <pre>
0086: * private void readObject(java.io.ObjectInputStream stream)
0087: * throws IOException, ClassNotFoundException;
0088: * private void writeObject(java.io.ObjectOutputStream stream)
0089: * throws IOException
0090: * </pre>
0091: *
0092: * <p>The writeObject method is responsible for writing the state of the object
0093: * for its particular class so that the corresponding readObject method can
0094: * restore it. The method does not need to concern itself with the state
0095: * belonging to the object's superclasses or subclasses. State is saved by
0096: * writing the individual fields to the ObjectOutputStream using the
0097: * writeObject method or by using the methods for primitive data types
0098: * supported by DataOutput.
0099: *
0100: * <p>Serialization does not write out the fields of any object that does not
0101: * implement the java.io.Serializable interface. Subclasses of Objects that
0102: * are not serializable can be serializable. In this case the non-serializable
0103: * class must have a no-arg constructor to allow its fields to be initialized.
0104: * In this case it is the responsibility of the subclass to save and restore
0105: * the state of the non-serializable class. It is frequently the case that the
0106: * fields of that class are accessible (public, package, or protected) or that
0107: * there are get and set methods that can be used to restore the state.
0108: *
0109: * <p>Serialization of an object can be prevented by implementing writeObject
0110: * and readObject methods that throw the NotSerializableException.
0111: * The exception will be caught by the ObjectOutputStream and abort the
0112: * serialization process.
0113: *
0114: * <p>Implementing the Externalizable interface allows the object to assume
0115: * complete control over the contents and format of the object's serialized
0116: * form. The methods of the Externalizable interface, writeExternal and
0117: * readExternal, are called to save and restore the objects state. When
0118: * implemented by a class they can write and read their own state using all of
0119: * the methods of ObjectOutput and ObjectInput. It is the responsibility of
0120: * the objects to handle any versioning that occurs.
0121: *
0122: * <p>Primitive data, excluding serializable fields and externalizable data, is
0123: * written to the ObjectOutputStream in block-data records. A block data record
0124: * is composed of a header and data. The block data header consists of a marker
0125: * and the number of bytes to follow the header. Consecutive primitive data
0126: * writes are merged into one block-data record. The blocking factor used for
0127: * a block-data record will be 1024 bytes. Each block-data record will be
0128: * filled up to 1024 bytes, or be written whenever there is a termination of
0129: * block-data mode. Calls to the ObjectOutputStream methods writeObject,
0130: * defaultWriteObject and writeFields initially terminate any existing
0131: * block-data record.
0132: *
0133: * @author Mike Warres
0134: * @author Roger Riggs
0135: * @version 1.105, 08/09/01
0136: * @see java.io.DataOutput
0137: * @see java.io.ObjectInputStream
0138: * @see java.io.Serializable
0139: * @see java.io.Externalizable
0140: * @see <a href="../../../guide/serialization/spec/output.doc.html"> Object Serialization Specification, Section 2, Object Output Classes</a>
0141: * @since JDK1.1
0142: */
0143: public class ObjectOutputStream extends OutputStream implements
0144: ObjectOutput, ObjectStreamConstants {
0145: /** cache of subclass security audit results */
0146: private static final SoftCache subclassAudits = new SoftCache(5);
0147:
0148: /** filter stream for handling block data conversion */
0149: private final BlockDataOutputStream bout;
0150: /** obj -> wire handle map */
0151: private final HandleTable handles;
0152: /** obj -> replacement obj map */
0153: private final ReplaceTable subs;
0154: /** stream protocol version */
0155: private int protocol = PROTOCOL_VERSION_2;
0156: /** recursion depth */
0157: private int depth;
0158:
0159: /** buffer for writing primitive field values */
0160: private byte[] primVals;
0161:
0162: /** if true, invoke writeObjectOverride() instead of writeObject() */
0163: private final boolean enableOverride;
0164: /** if true, invoke replaceObject() */
0165: private boolean enableReplace;
0166:
0167: // values below valid only during upcalls to writeObject()/writeExternal()
0168: /** object currently being serialized */
0169: private Object curObj;
0170: /** descriptor for current class (null if in writeExternal()) */
0171: private ObjectStreamClass curDesc;
0172: /** current PutField object */
0173: private PutFieldImpl curPut;
0174:
0175: /**
0176: * Creates an ObjectOutputStream that writes to the specified OutputStream.
0177: * This constructor writes the serialization stream header to the
0178: * underlying stream; callers may wish to flush the stream immediately to
0179: * ensure that constructors for receiving ObjectInputStreams will not block
0180: * when reading the header.
0181: *
0182: * <p>If a security manager is installed, this constructor will check for
0183: * the "enableSubclassImplementation" SerializablePermission when invoked
0184: * directly or indirectly by the constructor of a subclass which overrides
0185: * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
0186: * methods.
0187: *
0188: * @param out output stream to write to
0189: * @throws IOException if an I/O error occurs while writing stream header
0190: * @throws SecurityException if untrusted subclass illegally overrides
0191: * security-sensitive methods
0192: * @throws NullPointerException if <code>out</code> is <code>null</code>
0193: * @see ObjectOutputStream#ObjectOutputStream()
0194: * @see ObjectOutputStream#putFields()
0195: * @see ObjectInputStream#ObjectInputStream(InputStream)
0196: */
0197: public ObjectOutputStream(OutputStream out) throws IOException {
0198: verifySubclass();
0199: bout = new BlockDataOutputStream(out);
0200: handles = new HandleTable(10, (float) 3.00);
0201: subs = new ReplaceTable(10, (float) 3.00);
0202: enableOverride = false;
0203: writeStreamHeader();
0204: bout.setBlockDataMode(true);
0205: }
0206:
0207: /**
0208: * Provide a way for subclasses that are completely reimplementing
0209: * ObjectOutputStream to not have to allocate private data just used by
0210: * this implementation of ObjectOutputStream.
0211: *
0212: * <p>If there is a security manager installed, this method first calls the
0213: * security manager's <code>checkPermission</code> method with a
0214: * <code>SerializablePermission("enableSubclassImplementation")</code>
0215: * permission to ensure it's ok to enable subclassing.
0216: *
0217: * @exception IOException Thrown if not called by a subclass.
0218: *
0219: * @throws SecurityException
0220: * if a security manager exists and its
0221: * <code>checkPermission</code> method denies
0222: * enabling subclassing.
0223: *
0224: * @see SecurityManager#checkPermission
0225: * @see java.io.SerializablePermission
0226: */
0227: protected ObjectOutputStream() throws IOException,
0228: SecurityException {
0229: SecurityManager sm = System.getSecurityManager();
0230: if (sm != null) {
0231: sm
0232: .checkPermission(ObjectStreamConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
0233: }
0234: bout = null;
0235: handles = null;
0236: subs = null;
0237: enableOverride = true;
0238: }
0239:
0240: /**
0241: * Specify stream protocol version to use when writing the stream.
0242: *
0243: * <p>This routine provides a hook to enable the current version of
0244: * Serialization to write in a format that is backwards compatible to a
0245: * previous version of the stream format.
0246: *
0247: * <p>Every effort will be made to avoid introducing additional
0248: * backwards incompatibilities; however, sometimes there is no
0249: * other alternative.
0250: *
0251: * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
0252: * @throws IllegalStateException if called after any objects
0253: * have been serialized.
0254: * @throws IllegalArgumentException if invalid version is passed in.
0255: * @throws IOException if I/O errors occur
0256: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0257: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
0258: * @since 1.2
0259: */
0260: public void useProtocolVersion(int version) throws IOException {
0261: if (handles.size() != 0) {
0262: // TODO: implement better check for pristine stream?
0263: throw new IllegalStateException("stream non-empty");
0264: }
0265: switch (version) {
0266: case PROTOCOL_VERSION_1:
0267: case PROTOCOL_VERSION_2:
0268: protocol = version;
0269: break;
0270:
0271: default:
0272: throw new IllegalArgumentException("unknown version: "
0273: + version);
0274: }
0275: }
0276:
0277: /**
0278: * Write the specified object to the ObjectOutputStream. The class of the
0279: * object, the signature of the class, and the values of the non-transient
0280: * and non-static fields of the class and all of its supertypes are
0281: * written. Default serialization for a class can be overridden using the
0282: * writeObject and the readObject methods. Objects referenced by this
0283: * object are written transitively so that a complete equivalent graph of
0284: * objects can be reconstructed by an ObjectInputStream.
0285: *
0286: * <p>Exceptions are thrown for problems with the OutputStream and for
0287: * classes that should not be serialized. All exceptions are fatal to the
0288: * OutputStream, which is left in an indeterminate state, and it is up to
0289: * the caller to ignore or recover the stream state.
0290: *
0291: * @throws InvalidClassException Something is wrong with a class used by
0292: * serialization.
0293: * @throws NotSerializableException Some object to be serialized does not
0294: * implement the java.io.Serializable interface.
0295: * @throws IOException Any exception thrown by the underlying
0296: * OutputStream.
0297: */
0298: public final void writeObject(Object obj) throws IOException {
0299: if (enableOverride) {
0300: writeObjectOverride(obj);
0301: return;
0302: }
0303: try {
0304: writeObject0(obj, false);
0305: } catch (IOException ex) {
0306: if (depth == 0) {
0307: writeFatalException(ex);
0308: }
0309: throw ex;
0310: }
0311: }
0312:
0313: /**
0314: * Method used by subclasses to override the default writeObject method.
0315: * This method is called by trusted subclasses of ObjectInputStream
0316: * that constructed ObjectInputStream using the
0317: * protected no-arg constructor. The subclass is expected to provide
0318: * an override method with the modifier "final".
0319: *
0320: * @param obj object to be written to the underlying stream
0321: * @throws IOException if there are I/O errors while writing to the
0322: * underlying stream
0323: * @see #ObjectOutputStream()
0324: * @see #writeObject(Object)
0325: * @since 1.2
0326: */
0327: protected void writeObjectOverride(Object obj) throws IOException {
0328: }
0329:
0330: /**
0331: * Writes an "unshared" object to the ObjectOutputStream. This method is
0332: * identical to writeObject, except that it always writes the given object
0333: * as a new, unique object in the stream (as opposed to a back-reference
0334: * pointing to a previously serialized instance). Specifically:
0335: * <ul>
0336: * <li>An object written via writeUnshared is always serialized in the
0337: * same manner as a newly appearing object (an object that has not
0338: * been written to the stream yet), regardless of whether or not the
0339: * object has been written previously.
0340: *
0341: * <li>If writeObject is used to write an object that has been previously
0342: * written with writeUnshared, the previous writeUnshared operation
0343: * is treated as if it were a write of a separate object. In other
0344: * words, ObjectOutputStream will never generate back-references to
0345: * object data written by calls to writeUnshared.
0346: * </ul>
0347: * While writing an object via writeUnshared does not in itself guarantee a
0348: * unique reference to the object when it is deserialized, it allows a
0349: * single object to be defined multiple times in a stream, so that multiple
0350: * calls to readUnshared by the receiver will not conflict. Note that the
0351: * rules described above only apply to the base-level object written with
0352: * writeUnshared, and not to any transitively referenced sub-objects in the
0353: * object graph to be serialized.
0354: *
0355: * <p>ObjectOutputStream subclasses which override this method can only be
0356: * constructed in security contexts possessing the
0357: * "enableSubclassImplementation" SerializablePermission; any attempt to
0358: * instantiate such a subclass without this permission will cause a
0359: * SecurityException to be thrown.
0360: *
0361: * @param obj object to write to stream
0362: * @throws NotSerializableException if an object in the graph to be
0363: * serialized does not implement the Serializable interface
0364: * @throws InvalidClassException if a problem exists with the class of an
0365: * object to be serialized
0366: * @throws IOException if an I/O error occurs during serialization
0367: *
0368: */
0369: public void writeUnshared(Object obj) throws IOException {
0370: try {
0371: writeObject0(obj, true);
0372: } catch (IOException ex) {
0373: if (depth == 0) {
0374: writeFatalException(ex);
0375: }
0376: throw ex;
0377: }
0378: }
0379:
0380: /**
0381: * Write the non-static and non-transient fields of the current class
0382: * to this stream. This may only be called from the writeObject method
0383: * of the class being serialized. It will throw the NotActiveException
0384: * if it is called otherwise.
0385: *
0386: * @throws IOException if I/O errors occur while writing to the underlying
0387: * <code>OutputStream</code>
0388: */
0389: public void defaultWriteObject() throws IOException {
0390: if (curObj == null || curDesc == null) {
0391: throw new NotActiveException("not in call to writeObject");
0392: }
0393: bout.setBlockDataMode(false);
0394: defaultWriteFields(curObj, curDesc);
0395: bout.setBlockDataMode(true);
0396: }
0397:
0398: /**
0399: * Retrieve the object used to buffer persistent fields to be written to
0400: * the stream. The fields will be written to the stream when writeFields
0401: * method is called.
0402: *
0403: * @return an instance of the class Putfield that holds the serializable
0404: * fields
0405: * @throws IOException if I/O errors occur
0406: * @since 1.2
0407: */
0408: public ObjectOutputStream.PutField putFields() throws IOException {
0409: if (curPut == null) {
0410: if (curObj == null || curDesc == null) {
0411: throw new NotActiveException(
0412: "not in call to writeObject");
0413: }
0414: curPut = new PutFieldImpl(curDesc);
0415: }
0416: return curPut;
0417: }
0418:
0419: /**
0420: * Write the buffered fields to the stream.
0421: *
0422: * @throws IOException if I/O errors occur while writing to the underlying
0423: * stream
0424: * @throws NotActiveException Called when a classes writeObject
0425: * method was not called to write the state of the object.
0426: * @since 1.2
0427: */
0428: public void writeFields() throws IOException {
0429: if (curPut == null) {
0430: throw new NotActiveException("no current PutField object");
0431: }
0432: bout.setBlockDataMode(false);
0433: curPut.writeFields();
0434: bout.setBlockDataMode(true);
0435: }
0436:
0437: /**
0438: * Reset will disregard the state of any objects already written
0439: * to the stream. The state is reset to be the same as a new
0440: * ObjectOutputStream. The current point in the stream is marked
0441: * as reset so the corresponding ObjectInputStream will be reset
0442: * at the same point. Objects previously written to the stream
0443: * will not be refered to as already being in the stream. They
0444: * will be written to the stream again.
0445: *
0446: * @throws IOException if reset() is invoked while serializing an object.
0447: */
0448: public void reset() throws IOException {
0449: if (depth != 0) {
0450: throw new IOException("stream active");
0451: }
0452: bout.setBlockDataMode(false);
0453: bout.writeByte(TC_RESET);
0454: clear();
0455: bout.setBlockDataMode(true);
0456: }
0457:
0458: /**
0459: * Subclasses may implement this method to allow class data to be stored in
0460: * the stream. By default this method does nothing. The corresponding
0461: * method in ObjectInputStream is resolveClass. This method is called
0462: * exactly once for each unique class in the stream. The class name and
0463: * signature will have already been written to the stream. This method may
0464: * make free use of the ObjectOutputStream to save any representation of
0465: * the class it deems suitable (for example, the bytes of the class file).
0466: * The resolveClass method in the corresponding subclass of
0467: * ObjectInputStream must read and use any data or objects written by
0468: * annotateClass.
0469: *
0470: * @param cl the class to annotate custom data for
0471: * @throws IOException Any exception thrown by the underlying
0472: * OutputStream.
0473: */
0474: protected void annotateClass(Class cl) throws IOException {
0475: }
0476:
0477: /**
0478: * Subclasses may implement this method to store custom data in the
0479: * stream along with descriptors for dynamic proxy classes.
0480: *
0481: * <p>This method is called exactly once for each unique proxy class
0482: * descriptor in the stream. The default implementation of this
0483: * method in <code>ObjectOutputStream</code> does nothing.
0484: *
0485: * <p>The corresponding method in <code>ObjectInputStream</code> is
0486: * <code>resolveProxyClass</code>. For a given subclass of
0487: * <code>ObjectOutputStream</code> that overrides this method, the
0488: * <code>resolveProxyClass</code> method in the corresponding
0489: * subclass of <code>ObjectInputStream</code> must read any data or
0490: * objects writtem by <code>annotateProxyClass</code>.
0491: *
0492: * @param cl the proxy class to annotate custom data for
0493: * @throws IOException any exception thrown by the underlying
0494: * <code>OutputStream</code>
0495: * @see ObjectInputStream#resolveProxyClass(String[])
0496: * @since 1.3
0497: */
0498: protected void annotateProxyClass(Class cl) throws IOException {
0499: }
0500:
0501: /**
0502: * This method will allow trusted subclasses of ObjectOutputStream to
0503: * substitute one object for another during serialization. Replacing
0504: * objects is disabled until enableReplaceObject is called. The
0505: * enableReplaceObject method checks that the stream requesting to do
0506: * replacment can be trusted. The first occurrence of each object written
0507: * into the serialization stream is passed to replaceObject. Subsequent
0508: * references to the object are replaced by the object returned by the
0509: * original call to replaceObject. To ensure that the private state of
0510: * objects is not unintentionally exposed, only trusted streams may use
0511: * replaceObject.
0512: *
0513: * <p>The ObjectOutputStream.writeObject method takes a parameter of type
0514: * Object (as opposed to type Serializable) to allow for cases where
0515: * non-serializable objects are replaced by serializable ones.
0516: *
0517: * <p>When a subclass is replacing objects it must insure that either a
0518: * complementary substitution must be made during deserialization or that
0519: * the substituted object is compatible with every field where the
0520: * reference will be stored. Objects whose type is not a subclass of the
0521: * type of the field or array element abort the serialization by raising an
0522: * exception and the object is not be stored.
0523: *
0524: * <p>This method is called only once when each object is first
0525: * encountered. All subsequent references to the object will be redirected
0526: * to the new object. This method should return the object to be
0527: * substituted or the original object.
0528: *
0529: * <p>Null can be returned as the object to be substituted, but may cause
0530: * NullReferenceException in classes that contain references to the
0531: * original object since they may be expecting an object instead of
0532: * null.
0533: *
0534: * @param obj the object to be replaced
0535: * @return the alternate object that replaced the specified one
0536: * @throws IOException Any exception thrown by the underlying
0537: * OutputStream.
0538: */
0539: protected Object replaceObject(Object obj) throws IOException {
0540: return obj;
0541: }
0542:
0543: /**
0544: * Enable the stream to do replacement of objects in the stream.
0545: *
0546: * <p>When enabled, the replaceObject method is called for every object
0547: * being serialized.
0548: *
0549: * <p>If <i>enable</i> is true, and there is a security manager installed,
0550: * this method first calls the
0551: * security manager's <code>checkPermission</code> method with a
0552: * <code>SerializablePermission("enableSubstitution")</code>
0553: * permission to ensure it's ok to
0554: * enable the stream to do replacement of objects in the stream.
0555: *
0556: * @param enable boolean parameter to enable replacement of objects
0557: * @return the previous setting before this method was invoked
0558: * @throws SecurityException
0559: * if a security manager exists and its
0560: * <code>checkPermission</code> method denies
0561: * enabling the stream to do replacement of objects in the stream.
0562: *
0563: * @see SecurityManager#checkPermission
0564: * @see java.io.SerializablePermission
0565: */
0566: protected boolean enableReplaceObject(boolean enable)
0567: throws SecurityException {
0568: if (enable == enableReplace) {
0569: return enable;
0570: }
0571: if (enable) {
0572: SecurityManager sm = System.getSecurityManager();
0573: if (sm != null) {
0574: sm
0575: .checkPermission(ObjectStreamConstants.SUBSTITUTION_PERMISSION);
0576: }
0577: }
0578: enableReplace = enable;
0579: return !enableReplace;
0580: }
0581:
0582: /**
0583: * The writeStreamHeader method is provided so subclasses can
0584: * append or prepend their own header to the stream.
0585: * It writes the magic number and version to the stream.
0586: *
0587: * @throws IOException if I/O errors occur while writing to the underlying
0588: * stream
0589: */
0590: protected void writeStreamHeader() throws IOException {
0591: bout.writeShort(STREAM_MAGIC);
0592: bout.writeShort(STREAM_VERSION);
0593: }
0594:
0595: /**
0596: * Write the specified class descriptor to the ObjectOutputStream. Class
0597: * descriptors are used to identify the classes of objects written to the
0598: * stream. Subclasses of ObjectOutputStream may override this method to
0599: * customize the way in which class descriptors are written to the
0600: * serialization stream. The corresponding method in ObjectInputStream,
0601: * <code>readClassDescriptor</code>, should then be overridden to
0602: * reconstitute the class descriptor from its custom stream representation.
0603: * By default, this method writes class descriptors according to the format
0604: * defined in the Object Serialization specification.
0605: *
0606: * <p>Note that this method will only be called if the ObjectOutputStream
0607: * is not using the old serialization stream format (set by calling
0608: * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
0609: * serialization stream is using the old format
0610: * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
0611: * internally in a manner that cannot be overridden or customized.
0612: *
0613: * @param desc class descriptor to write to the stream
0614: * @throws IOException If an I/O error has occurred.
0615: * @see java.io.ObjectInputStream#readClassDescriptor()
0616: * @see #useProtocolVersion(int)
0617: * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0618: * @since 1.3
0619: */
0620: protected void writeClassDescriptor(ObjectStreamClass desc)
0621: throws IOException {
0622: desc.writeNonProxy(this );
0623: }
0624:
0625: /**
0626: * Writes a byte. This method will block until the byte is actually
0627: * written.
0628: *
0629: * @param val the byte to be written to the stream
0630: * @throws IOException If an I/O error has occurred.
0631: */
0632: public void write(int val) throws IOException {
0633: bout.write(val);
0634: }
0635:
0636: /**
0637: * Writes an array of bytes. This method will block until the bytes
0638: * are actually written.
0639: *
0640: * @param buf the data to be written
0641: * @exception IOException If an I/O error has occurred.
0642: */
0643: public void write(byte[] buf) throws IOException {
0644: bout.write(buf, 0, buf.length, false);
0645: }
0646:
0647: /**
0648: * Writes a sub array of bytes.
0649: *
0650: * @param buf the data to be written
0651: * @param off the start offset in the data
0652: * @param len the number of bytes that are written
0653: * @param copyOnWrite do not expose b to overrides of ObjectStream.write,
0654: * copy the contents of b to a buffer before writing.
0655: * @exception IOException If an I/O error has occurred.
0656: */
0657: public void write(byte[] buf, int off, int len) throws IOException {
0658: int endoff = off + len;
0659: if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
0660: throw new IndexOutOfBoundsException();
0661: }
0662: bout.write(buf, off, len, false);
0663: }
0664:
0665: /**
0666: * Flushes the stream. This will write any buffered output bytes and flush
0667: * through to the underlying stream.
0668: *
0669: * @throws IOException If an I/O error has occurred.
0670: */
0671: public void flush() throws IOException {
0672: bout.flush();
0673: }
0674:
0675: /**
0676: * Drain any buffered data in ObjectOutputStream. Similar to flush but
0677: * does not propagate the flush to the underlying stream.
0678: *
0679: * @throws IOException if I/O errors occur while writing to the underlying
0680: * stream
0681: */
0682: protected void drain() throws IOException {
0683: bout.drain();
0684: }
0685:
0686: /**
0687: * Closes the stream. This method must be called to release any resources
0688: * associated with the stream.
0689: *
0690: * @throws IOException If an I/O error has occurred.
0691: */
0692: public void close() throws IOException {
0693: flush();
0694: clear();
0695: bout.close();
0696: }
0697:
0698: /**
0699: * Writes a boolean.
0700: *
0701: * @param val the boolean to be written
0702: * @throws IOException if I/O errors occur while writing to the underlying
0703: * stream
0704: */
0705: public void writeBoolean(boolean val) throws IOException {
0706: bout.writeBoolean(val);
0707: }
0708:
0709: /**
0710: * Writes an 8 bit byte.
0711: *
0712: * @param val the byte value to be written
0713: * @throws IOException if I/O errors occur while writing to the underlying
0714: * stream
0715: */
0716: public void writeByte(int val) throws IOException {
0717: bout.writeByte(val);
0718: }
0719:
0720: /**
0721: * Writes a 16 bit short.
0722: *
0723: * @param val the short value to be written
0724: * @throws IOException if I/O errors occur while writing to the underlying
0725: * stream
0726: */
0727: public void writeShort(int val) throws IOException {
0728: bout.writeShort(val);
0729: }
0730:
0731: /**
0732: * Writes a 16 bit char.
0733: *
0734: * @param val the char value to be written
0735: * @throws IOException if I/O errors occur while writing to the underlying
0736: * stream
0737: */
0738: public void writeChar(int val) throws IOException {
0739: bout.writeChar(val);
0740: }
0741:
0742: /**
0743: * Writes a 32 bit int.
0744: *
0745: * @param val the integer value to be written
0746: * @throws IOException if I/O errors occur while writing to the underlying
0747: * stream
0748: */
0749: public void writeInt(int val) throws IOException {
0750: bout.writeInt(val);
0751: }
0752:
0753: /**
0754: * Writes a 64 bit long.
0755: *
0756: * @param val the long value to be written
0757: * @throws IOException if I/O errors occur while writing to the underlying
0758: * stream
0759: */
0760: public void writeLong(long val) throws IOException {
0761: bout.writeLong(val);
0762: }
0763:
0764: /**
0765: * Writes a 32 bit float.
0766: *
0767: * @param val the float value to be written
0768: * @throws IOException if I/O errors occur while writing to the underlying
0769: * stream
0770: */
0771: public void writeFloat(float val) throws IOException {
0772: bout.writeFloat(val);
0773: }
0774:
0775: /**
0776: * Writes a 64 bit double.
0777: *
0778: * @param val the double value to be written
0779: * @throws IOException if I/O errors occur while writing to the underlying
0780: * stream
0781: */
0782: public void writeDouble(double val) throws IOException {
0783: bout.writeDouble(val);
0784: }
0785:
0786: /**
0787: * Writes a String as a sequence of bytes.
0788: *
0789: * @param str the String of bytes to be written
0790: * @throws IOException if I/O errors occur while writing to the underlying
0791: * stream
0792: */
0793: public void writeBytes(String str) throws IOException {
0794: bout.writeBytes(str);
0795: }
0796:
0797: /**
0798: * Writes a String as a sequence of chars.
0799: *
0800: * @param str the String of chars to be written
0801: * @throws IOException if I/O errors occur while writing to the underlying
0802: * stream
0803: */
0804: public void writeChars(String str) throws IOException {
0805: bout.writeChars(str);
0806: }
0807:
0808: /**
0809: * Primitive data write of this String in UTF format.
0810: *
0811: * Note that there is a significant difference between
0812: * writing a String into the stream as primitive data or
0813: * as an Object. A String instance written by writeObject
0814: * is written into the stream as a String initially. Future
0815: * writeObject() calls write references to the string into
0816: * the stream.
0817: *
0818: * @param str the String in UTF format
0819: * @throws IOException if I/O errors occur while writing to the underlying
0820: * stream
0821: */
0822: public void writeUTF(String str) throws IOException {
0823: bout.writeUTF(str);
0824: }
0825:
0826: /*
0827: * Gets the values of the primitive fields of object obj. fieldIDs is an
0828: * array of field IDs (the primFieldsID field of the appropriate
0829: * ObjectStreamClass) identifying which fields to get. typecodes is an
0830: * array of characters designating the primitive type of each field (e.g.,
0831: * 'C' for char, 'Z' for boolean, etc.) data is the byte buffer in which
0832: * the primitive field values are written, in the order of their field IDs.
0833: *
0834: * For efficiency, this method does not check all of its arguments for
0835: * safety. Specifically, it assumes that obj's type is compatible with the
0836: * given field IDs, and that the data array is long enough to contain all of
0837: * the byte values that will be written to it.
0838: */
0839: static native void getPrimitiveFieldValues(Object obj,
0840: long[] fieldIDs, char[] typecodes, byte[] data);
0841:
0842: /*
0843: * Gets the value of an object field of object obj. fieldID is the field ID
0844: * identifying which field to set (obtained from the objFieldsID array field
0845: * of the appropriate ObjectStreamClass).
0846: *
0847: * For efficiency, this method does not check to make sure that obj's type
0848: * is compatible with the given field ID.
0849: */
0850: static native Object getObjectFieldValue(Object obj, long fieldID);
0851:
0852: /*************************************/
0853:
0854: /**
0855: * Provide programatic access to the persistent fields to be written
0856: * to ObjectOutput.
0857: *
0858: * @since 1.2
0859: */
0860: public static abstract class PutField {
0861:
0862: /**
0863: * Put the value of the named boolean field into the persistent field.
0864: *
0865: * @param name the name of the serializable field
0866: * @param val the value to assign to the field
0867: */
0868: public abstract void put(String name, boolean val);
0869:
0870: /**
0871: * Put the value of the named byte field into the persistent field.
0872: *
0873: * @param name the name of the serializable field
0874: * @param val the value to assign to the field
0875: */
0876: public abstract void put(String name, byte val);
0877:
0878: /**
0879: * Put the value of the named char field into the persistent field.
0880: *
0881: * @param name the name of the serializable field
0882: * @param val the value to assign to the field
0883: */
0884: public abstract void put(String name, char val);
0885:
0886: /**
0887: * Put the value of the named short field into the persistent field.
0888: *
0889: * @param name the name of the serializable field
0890: * @param val the value to assign to the field
0891: */
0892: public abstract void put(String name, short val);
0893:
0894: /**
0895: * Put the value of the named int field into the persistent field.
0896: *
0897: * @param name the name of the serializable field
0898: * @param val the value to assign to the field
0899: */
0900: public abstract void put(String name, int val);
0901:
0902: /**
0903: * Put the value of the named long field into the persistent field.
0904: *
0905: * @param name the name of the serializable field
0906: * @param val the value to assign to the field
0907: */
0908: public abstract void put(String name, long val);
0909:
0910: /**
0911: * Put the value of the named float field into the persistent field.
0912: *
0913: * @param name the name of the serializable field
0914: * @param val the value to assign to the field
0915: */
0916: public abstract void put(String name, float val);
0917:
0918: /**
0919: * Put the value of the named double field into the persistent field.
0920: *
0921: * @param name the name of the serializable field
0922: * @param val the value to assign to the field
0923: */
0924: public abstract void put(String name, double val);
0925:
0926: /**
0927: * Put the value of the named Object field into the persistent field.
0928: *
0929: * @param name the name of the serializable field
0930: * @param val the value to assign to the field
0931: */
0932: public abstract void put(String name, Object val);
0933:
0934: /**
0935: * Write the data and fields to the specified ObjectOutput stream.
0936: *
0937: * @param out the stream to write the data and fields to
0938: * @throws IOException if I/O errors occur while writing to the
0939: * underlying stream
0940: */
0941: public abstract void write(ObjectOutput out) throws IOException;
0942: }
0943:
0944: /**
0945: * Returns protocol version in use.
0946: */
0947: int getProtocolVersion() {
0948: return protocol;
0949: }
0950:
0951: /**
0952: * Writes string without allowing it to be replaced in stream. Used by
0953: * ObjectStreamClass to write class descriptor type strings.
0954: */
0955: void writeTypeString(String str) throws IOException {
0956: int handle;
0957: if (str == null) {
0958: writeNull();
0959: } else if ((handle = handles.lookup(str)) != -1) {
0960: writeHandle(handle);
0961: } else {
0962: writeString(str, false);
0963: }
0964: }
0965:
0966: /**
0967: * Verifies that this (possibly subclass) instance can be constructed
0968: * without violating security constraints: the subclass must not override
0969: * security-sensitive non-final methods, or else the
0970: * "enableSubclassImplementation" SerializablePermission is checked.
0971: */
0972: private void verifySubclass() {
0973: Class cl = getClass();
0974: synchronized (subclassAudits) {
0975: Boolean result = (Boolean) subclassAudits.get(cl);
0976: if (result == null) {
0977: //
0978: // Note: only new Boolean instances (i.e., not Boolean.TRUE or
0979: // Boolean.FALSE) must be used as cache values, otherwise cache
0980: // entry will pin associated class.
0981: //
0982: result = new Boolean(auditSubclass(cl));
0983: subclassAudits.put(cl, result);
0984: }
0985: if (result.booleanValue()) {
0986: return;
0987: }
0988: }
0989: SecurityManager sm = System.getSecurityManager();
0990: if (sm != null) {
0991: sm
0992: .checkPermission(ObjectStreamConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
0993: }
0994: }
0995:
0996: /**
0997: * Performs reflective checks on given subclass to verify that it doesn't
0998: * override security-sensitive non-final methods. Returns true if subclass
0999: * is "safe", false otherwise.
1000: */
1001: private static boolean auditSubclass(final Class subcl) {
1002: Boolean result = (Boolean) AccessController
1003: .doPrivileged(new PrivilegedAction() {
1004: public Object run() {
1005: for (Class cl = subcl; cl != ObjectOutputStream.class; cl = cl
1006: .getSuperclass()) {
1007: try {
1008: cl.getDeclaredMethod("writeUnshared",
1009: new Class[] { Object.class });
1010: return Boolean.FALSE;
1011: } catch (NoSuchMethodException ex) {
1012: }
1013: try {
1014: cl.getDeclaredMethod("putFields",
1015: new Class[0]);
1016: return Boolean.FALSE;
1017: } catch (NoSuchMethodException ex) {
1018: }
1019: }
1020: return Boolean.TRUE;
1021: }
1022: });
1023: return result.booleanValue();
1024: }
1025:
1026: /**
1027: * Clears internal data structures.
1028: */
1029: private void clear() {
1030: subs.clear();
1031: handles.clear();
1032: }
1033:
1034: /**
1035: * Underlying writeObject/writeUnshared implementation.
1036: */
1037: private void writeObject0(Object obj, boolean unshared)
1038: throws IOException {
1039: boolean oldMode = bout.setBlockDataMode(false);
1040: depth++;
1041: try {
1042: // handle previously written and non-replaceable objects
1043: int h;
1044: if ((obj = subs.lookup(obj)) == null) {
1045: writeNull();
1046: return;
1047: } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1048: writeHandle(h);
1049: return;
1050: } else if (obj instanceof Class) {
1051: writeClass((Class) obj, unshared);
1052: return;
1053: } else if (obj instanceof ObjectStreamClass) {
1054: writeClassDesc((ObjectStreamClass) obj, unshared);
1055: return;
1056: }
1057:
1058: // check for replacement object
1059: Object orig = obj;
1060: Class cl = obj.getClass();
1061: ObjectStreamClass desc;
1062: for (;;) {
1063: // TODO: skip this check for strings/arrays?
1064: Class repCl;
1065: desc = ObjectStreamClass.lookup(cl, true);
1066: if (!desc.hasWriteReplaceMethod()
1067: || (obj = desc.invokeWriteReplace(obj)) == null
1068: || (repCl = obj.getClass()) == cl) {
1069: break;
1070: }
1071: cl = repCl;
1072: }
1073: if (enableReplace) {
1074: Object rep = replaceObject(obj);
1075: if (rep != obj && rep != null) {
1076: cl = rep.getClass();
1077: desc = ObjectStreamClass.lookup(cl, true);
1078: }
1079: obj = rep;
1080: }
1081:
1082: // if object replaced, run through original checks a second time
1083: if (obj != orig) {
1084: subs.assign(orig, obj);
1085: if (obj == null) {
1086: writeNull();
1087: return;
1088: } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1089: writeHandle(h);
1090: return;
1091: } else if (obj instanceof Class) {
1092: writeClass((Class) obj, unshared);
1093: return;
1094: } else if (obj instanceof ObjectStreamClass) {
1095: writeClassDesc((ObjectStreamClass) obj, unshared);
1096: return;
1097: }
1098: }
1099:
1100: // remaining cases
1101: if (obj instanceof String) {
1102: writeString((String) obj, unshared);
1103: } else if (cl.isArray()) {
1104: writeArray(obj, desc, unshared);
1105: } else if (obj instanceof Serializable) {
1106: writeOrdinaryObject(obj, desc, unshared);
1107: } else {
1108: throw new NotSerializableException(cl.getName());
1109: }
1110: } finally {
1111: depth--;
1112: bout.setBlockDataMode(oldMode);
1113: }
1114: }
1115:
1116: /**
1117: * Writes null code to stream.
1118: */
1119: private void writeNull() throws IOException {
1120: bout.writeByte(TC_NULL);
1121: }
1122:
1123: /**
1124: * Writes given object handle to stream.
1125: */
1126: private void writeHandle(int handle) throws IOException {
1127: bout.writeByte(TC_REFERENCE);
1128: bout.writeInt(baseWireHandle + handle);
1129: }
1130:
1131: /**
1132: * Writes representation of given class to stream.
1133: */
1134: private void writeClass(Class cl, boolean unshared)
1135: throws IOException {
1136: bout.writeByte(TC_CLASS);
1137: writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1138: handles.assign(unshared ? null : cl);
1139: }
1140:
1141: /**
1142: * Writes representation of given class descriptor to stream.
1143: */
1144: private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1145: throws IOException {
1146: int handle;
1147: if (desc == null) {
1148: writeNull();
1149: } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1150: writeHandle(handle);
1151: } else if (desc.isProxy()) {
1152: writeProxyDesc(desc, unshared);
1153: } else {
1154: writeNonProxyDesc(desc, unshared);
1155: }
1156: }
1157:
1158: /**
1159: * Writes class descriptor representing a dynamic proxy class to stream.
1160: */
1161: private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1162: throws IOException {
1163: bout.writeByte(TC_PROXYCLASSDESC);
1164: handles.assign(unshared ? null : desc);
1165:
1166: Class cl = desc.forClass();
1167: Class[] ifaces = cl.getInterfaces();
1168: bout.writeInt(ifaces.length);
1169: for (int i = 0; i < ifaces.length; i++) {
1170: bout.writeUTF(ifaces[i].getName());
1171: }
1172:
1173: bout.setBlockDataMode(true);
1174: annotateProxyClass(cl);
1175: bout.setBlockDataMode(false);
1176: bout.writeByte(TC_ENDBLOCKDATA);
1177:
1178: writeClassDesc(desc.getSuperDesc(), false);
1179: }
1180:
1181: /**
1182: * Writes class descriptor representing a standard (i.e., not a dynamic
1183: * proxy) class to stream.
1184: */
1185: private void writeNonProxyDesc(ObjectStreamClass desc,
1186: boolean unshared) throws IOException {
1187: bout.writeByte(TC_CLASSDESC);
1188: handles.assign(unshared ? null : desc);
1189:
1190: if (protocol == PROTOCOL_VERSION_1) {
1191: // do not invoke class descriptor write hook with old protocol
1192: desc.writeNonProxy(this );
1193: } else {
1194: writeClassDescriptor(desc);
1195: }
1196:
1197: Class cl = desc.forClass();
1198: bout.setBlockDataMode(true);
1199: annotateClass(cl);
1200: bout.setBlockDataMode(false);
1201: bout.writeByte(TC_ENDBLOCKDATA);
1202:
1203: writeClassDesc(desc.getSuperDesc(), false);
1204: }
1205:
1206: /**
1207: * Writes given string to stream, using standard or long UTF format
1208: * depending on string length.
1209: */
1210: private void writeString(String str, boolean unshared)
1211: throws IOException {
1212: handles.assign(unshared ? null : str);
1213: long utflen = bout.getUTFLength(str);
1214: if (utflen <= 0xFFFF) {
1215: bout.writeByte(TC_STRING);
1216: bout.writeUTF(str, utflen);
1217: } else {
1218: bout.writeByte(TC_LONGSTRING);
1219: bout.writeLongUTF(str, utflen);
1220: }
1221: }
1222:
1223: /**
1224: * Writes given array object to stream.
1225: */
1226: private void writeArray(Object array, ObjectStreamClass desc,
1227: boolean unshared) throws IOException {
1228: bout.writeByte(TC_ARRAY);
1229: writeClassDesc(desc, false);
1230: handles.assign(unshared ? null : array);
1231:
1232: Class ccl = desc.forClass().getComponentType();
1233: if (ccl.isPrimitive()) {
1234: if (ccl == Integer.TYPE) {
1235: int[] ia = (int[]) array;
1236: bout.writeInt(ia.length);
1237: bout.writeInts(ia, 0, ia.length);
1238: } else if (ccl == Byte.TYPE) {
1239: byte[] ba = (byte[]) array;
1240: bout.writeInt(ba.length);
1241: bout.write(ba, 0, ba.length, true);
1242: } else if (ccl == Long.TYPE) {
1243: long[] ja = (long[]) array;
1244: bout.writeInt(ja.length);
1245: bout.writeLongs(ja, 0, ja.length);
1246: } else if (ccl == Float.TYPE) {
1247: float[] fa = (float[]) array;
1248: bout.writeInt(fa.length);
1249: bout.writeFloats(fa, 0, fa.length);
1250: } else if (ccl == Double.TYPE) {
1251: double[] da = (double[]) array;
1252: bout.writeInt(da.length);
1253: bout.writeDoubles(da, 0, da.length);
1254: } else if (ccl == Short.TYPE) {
1255: short[] sa = (short[]) array;
1256: bout.writeInt(sa.length);
1257: bout.writeShorts(sa, 0, sa.length);
1258: } else if (ccl == Character.TYPE) {
1259: char[] ca = (char[]) array;
1260: bout.writeInt(ca.length);
1261: bout.writeChars(ca, 0, ca.length);
1262: } else if (ccl == Boolean.TYPE) {
1263: boolean[] za = (boolean[]) array;
1264: bout.writeInt(za.length);
1265: bout.writeBooleans(za, 0, za.length);
1266: } else {
1267: throw new InternalError();
1268: }
1269: } else {
1270: Object[] objs = (Object[]) array;
1271: int len = objs.length;
1272: bout.writeInt(len);
1273: for (int i = 0; i < len; i++) {
1274: writeObject0(objs[i], false);
1275: }
1276: }
1277: }
1278:
1279: /**
1280: * Writes representation of a "ordinary" (i.e., not a String, Class,
1281: * ObjectStreamClass or array) serializable object to the stream.
1282: */
1283: private void writeOrdinaryObject(Object obj,
1284: ObjectStreamClass desc, boolean unshared)
1285: throws IOException {
1286: bout.writeByte(TC_OBJECT);
1287: writeClassDesc(desc, false);
1288: handles.assign(unshared ? null : obj);
1289:
1290: if (desc.isExternalizable() && !desc.isProxy()) {
1291: writeExternalData((Externalizable) obj);
1292: } else {
1293: writeSerialData(obj, desc);
1294: }
1295: }
1296:
1297: /**
1298: * Writes externalizable data of given object by invoking its
1299: * writeExternal() method.
1300: */
1301: private void writeExternalData(Externalizable obj)
1302: throws IOException {
1303: Object oldObj = curObj;
1304: ObjectStreamClass oldDesc = curDesc;
1305: PutFieldImpl oldPut = curPut;
1306: curObj = obj;
1307: curDesc = null;
1308: curPut = null;
1309:
1310: if (protocol == PROTOCOL_VERSION_1) {
1311: obj.writeExternal(this );
1312: } else {
1313: bout.setBlockDataMode(true);
1314: obj.writeExternal(this );
1315: bout.setBlockDataMode(false);
1316: bout.writeByte(TC_ENDBLOCKDATA);
1317: }
1318:
1319: curObj = oldObj;
1320: curDesc = oldDesc;
1321: curPut = oldPut;
1322: }
1323:
1324: /**
1325: * Writes instance data for each serializable class of given object, from
1326: * superclass to subclass.
1327: */
1328: private void writeSerialData(Object obj, ObjectStreamClass desc)
1329: throws IOException {
1330: ObjectStreamClass.ClassDataSlot[] slots = desc
1331: .getClassDataLayout();
1332: for (int i = 0; i < slots.length; i++) {
1333: ObjectStreamClass slotDesc = slots[i].desc;
1334: if (slotDesc.hasWriteObjectMethod()) {
1335: Object oldObj = curObj;
1336: ObjectStreamClass oldDesc = curDesc;
1337: PutFieldImpl oldPut = curPut;
1338: curObj = obj;
1339: curDesc = slotDesc;
1340: curPut = null;
1341:
1342: bout.setBlockDataMode(true);
1343: slotDesc.invokeWriteObject(obj, this );
1344: bout.setBlockDataMode(false);
1345: bout.writeByte(TC_ENDBLOCKDATA);
1346:
1347: curObj = oldObj;
1348: curDesc = oldDesc;
1349: curPut = oldPut;
1350: } else {
1351: defaultWriteFields(obj, slotDesc);
1352: }
1353: }
1354: }
1355:
1356: /**
1357: * Fetches and writes values of serializable fields of given object to
1358: * stream. The given class descriptor specifies which field values to
1359: * write, and in which order they should be written.
1360: */
1361: private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1362: throws IOException {
1363: // TODO: perform conservative isInstance check here?
1364: desc.checkDefaultSerialize();
1365:
1366: int primDataSize = desc.getPrimDataSize();
1367: if (primVals == null || primVals.length < primDataSize) {
1368: primVals = new byte[primDataSize];
1369: }
1370: desc.getPrimFieldValues(obj, primVals);
1371: bout.write(primVals, 0, primDataSize, false);
1372:
1373: ObjectStreamField[] fields = desc.getFields(false);
1374: Object[] objVals = new Object[desc.getNumObjFields()];
1375: int numPrimFields = fields.length - objVals.length;
1376: desc.getObjFieldValues(obj, objVals);
1377: for (int i = 0; i < objVals.length; i++) {
1378: writeObject0(objVals[i], fields[numPrimFields + i]
1379: .isUnshared());
1380: }
1381: }
1382:
1383: /**
1384: * Attempts to write to stream fatal IOException that has caused
1385: * serialization to abort.
1386: */
1387: private void writeFatalException(IOException ex) throws IOException {
1388: /*
1389: * Note: the serialization specification states that if a second
1390: * IOException occurs while attempting to serialize the original fatal
1391: * exception to the stream, then a StreamCorruptedException should be
1392: * thrown (section 2.1). However, due to a bug in previous
1393: * implementations of serialization, StreamCorruptedExceptions were
1394: * rarely (if ever) actually thrown--the "root" exceptions from
1395: * underlying streams were thrown instead. This historical behavior is
1396: * followed here for consistency.
1397: */
1398: clear();
1399: boolean oldMode = bout.setBlockDataMode(false);
1400: try {
1401: bout.writeByte(TC_EXCEPTION);
1402: writeObject0(ex, false);
1403: clear();
1404: } finally {
1405: bout.setBlockDataMode(oldMode);
1406: }
1407: }
1408:
1409: /**
1410: * Converts specified span of float values into byte values.
1411: */
1412: // TODO: remove once hotspot inlines Float.floatToIntBits
1413: private static native void floatsToBytes(float[] src, int srcpos,
1414: byte[] dst, int dstpos, int nfloats);
1415:
1416: /**
1417: * Converts specified span of double values into byte values.
1418: */
1419: // TODO: remove once hotspot inlines Double.doubleToLongBits
1420: private static native void doublesToBytes(double[] src, int srcpos,
1421: byte[] dst, int dstpos, int ndoubles);
1422:
1423: /**
1424: * Default PutField implementation.
1425: */
1426: private class PutFieldImpl extends PutField {
1427:
1428: /** class descriptor describing serializable fields */
1429: private final ObjectStreamClass desc;
1430: /** primitive field values */
1431: private final byte[] primVals;
1432: /** object field values */
1433: private final Object[] objVals;
1434:
1435: /**
1436: * Creates PutFieldImpl object for writing fields defined in given
1437: * class descriptor.
1438: */
1439: PutFieldImpl(ObjectStreamClass desc) {
1440: this .desc = desc;
1441: primVals = new byte[desc.getPrimDataSize()];
1442: objVals = new Object[desc.getNumObjFields()];
1443: }
1444:
1445: public void put(String name, boolean val) {
1446: Bits.putBoolean(primVals,
1447: getFieldOffset(name, Boolean.TYPE), val);
1448: }
1449:
1450: public void put(String name, byte val) {
1451: primVals[getFieldOffset(name, Byte.TYPE)] = val;
1452: }
1453:
1454: public void put(String name, char val) {
1455: Bits.putChar(primVals,
1456: getFieldOffset(name, Character.TYPE), val);
1457: }
1458:
1459: public void put(String name, short val) {
1460: Bits.putShort(primVals, getFieldOffset(name, Short.TYPE),
1461: val);
1462: }
1463:
1464: public void put(String name, int val) {
1465: Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE),
1466: val);
1467: }
1468:
1469: public void put(String name, float val) {
1470: Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE),
1471: val);
1472: }
1473:
1474: public void put(String name, long val) {
1475: Bits
1476: .putLong(primVals, getFieldOffset(name, Long.TYPE),
1477: val);
1478: }
1479:
1480: public void put(String name, double val) {
1481: Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE),
1482: val);
1483: }
1484:
1485: public void put(String name, Object val) {
1486: objVals[getFieldOffset(name, Object.class)] = val;
1487: }
1488:
1489: // deprecated in ObjectOutputStream.PutField
1490: public void write(ObjectOutput out) throws IOException {
1491: /*
1492: * Applications should *not* use this method to write PutField
1493: * data, as it will lead to stream corruption if the PutField
1494: * object writes any primitive data (since block data mode is not
1495: * unset/set properly, as is done in OOS.writeFields()). This
1496: * broken implementation is being retained solely for behavioral
1497: * compatibility, in order to support applications which use
1498: * OOS.PutField.write() for writing only non-primitive data.
1499: *
1500: * Serialization of unshared objects is not implemented here since
1501: * it is not necessary for backwards compatibility; also, unshared
1502: * semantics may not be supported by the given ObjectOutput
1503: * instance. Applications which write unshared objects using the
1504: * PutField API must use OOS.writeFields().
1505: */
1506: out.write(primVals, 0, primVals.length);
1507:
1508: ObjectStreamField[] fields = desc.getFields(false);
1509: int numPrimFields = fields.length - objVals.length;
1510: // TODO: warn if numPrimFields > 0?
1511: for (int i = 0; i < objVals.length; i++) {
1512: if (fields[numPrimFields + i].isUnshared()) {
1513: throw new IOException(
1514: "cannot write unshared object");
1515: }
1516: out.writeObject(objVals[i]);
1517: }
1518: }
1519:
1520: /**
1521: * Writes buffered primitive data and object fields to stream.
1522: */
1523: void writeFields() throws IOException {
1524: bout.write(primVals, 0, primVals.length, false);
1525:
1526: ObjectStreamField[] fields = desc.getFields(false);
1527: int numPrimFields = fields.length - objVals.length;
1528: for (int i = 0; i < objVals.length; i++) {
1529: writeObject0(objVals[i], fields[numPrimFields + i]
1530: .isUnshared());
1531: }
1532: }
1533:
1534: /**
1535: * Returns offset of field with given name and type. A specified type
1536: * of null matches all types, Object.class matches all non-primitive
1537: * types, and any other non-null type matches assignable types only.
1538: * Throws IllegalArgumentException if no matching field found.
1539: */
1540: private int getFieldOffset(String name, Class type) {
1541: ObjectStreamField field = desc.getField(name, type);
1542: if (field == null) {
1543: throw new IllegalArgumentException("no such field");
1544: }
1545: return field.getOffset();
1546: }
1547: }
1548:
1549: /**
1550: * Buffered output stream with two modes: in default mode, outputs data in
1551: * same format as DataOutputStream; in "block data" mode, outputs data
1552: * bracketed by block data markers (see object serialization specification
1553: * for details).
1554: */
1555: private static class BlockDataOutputStream extends OutputStream
1556: implements DataOutput {
1557: /** maximum data block length */
1558: private static final int MAX_BLOCK_SIZE = 1024;
1559: /** maximum data block header length */
1560: private static final int MAX_HEADER_SIZE = 5;
1561: /** (tunable) length of char buffer (for writing strings) */
1562: private static final int CHAR_BUF_SIZE = 256;
1563:
1564: /** buffer for writing general/block data */
1565: private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1566: /** buffer for writing block data headers */
1567: private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1568: /** char buffer for fast string writes */
1569: private final char[] cbuf = new char[CHAR_BUF_SIZE];
1570:
1571: /** block data mode */
1572: private boolean blkmode = false;
1573: /** current offset into buf */
1574: private int pos = 0;
1575:
1576: /** underlying output stream */
1577: private final OutputStream out;
1578: /** loopback stream (for data writes that span data blocks) */
1579: private final DataOutputStream dout;
1580:
1581: /**
1582: * Creates new BlockDataOutputStream on top of given underlying stream.
1583: * Block data mode is turned off by default.
1584: */
1585: BlockDataOutputStream(OutputStream out) {
1586: this .out = out;
1587: dout = new DataOutputStream(this );
1588: }
1589:
1590: /**
1591: * Sets block data mode to the given mode (true == on, false == off)
1592: * and returns the previous mode value. If the new mode is the same as
1593: * the old mode, no action is taken. If the new mode differs from the
1594: * old mode, any buffered data is flushed before switching to the new
1595: * mode.
1596: */
1597: boolean setBlockDataMode(boolean mode) throws IOException {
1598: if (blkmode == mode) {
1599: return blkmode;
1600: }
1601: drain();
1602: blkmode = mode;
1603: return !blkmode;
1604: }
1605:
1606: /**
1607: * Returns true if the stream is currently in block data mode, false
1608: * otherwise.
1609: */
1610: boolean getBlockDataMode() {
1611: return blkmode;
1612: }
1613:
1614: /* ----------------- generic output stream methods ----------------- */
1615: /*
1616: * The following methods are equivalent to their counterparts in
1617: * OutputStream, except that they partition written data into data
1618: * blocks when in block data mode.
1619: */
1620:
1621: public void write(int b) throws IOException {
1622: if (pos >= MAX_BLOCK_SIZE) {
1623: drain();
1624: }
1625: buf[pos++] = (byte) b;
1626: }
1627:
1628: public void write(byte[] b) throws IOException {
1629: write(b, 0, b.length, false);
1630: }
1631:
1632: public void write(byte[] b, int off, int len)
1633: throws IOException {
1634: write(b, off, len, false);
1635: }
1636:
1637: public void flush() throws IOException {
1638: drain();
1639: out.flush();
1640: }
1641:
1642: public void close() throws IOException {
1643: flush();
1644: out.close();
1645: }
1646:
1647: /**
1648: * Writes specified span of byte values from given array. If copy is
1649: * true, copies the values to an intermediate buffer before writing
1650: * them to underlying stream (to avoid exposing a reference to the
1651: * original byte array).
1652: */
1653: void write(byte[] b, int off, int len, boolean copy)
1654: throws IOException {
1655: if (!(copy || blkmode)) { // write directly
1656: drain();
1657: out.write(b, off, len);
1658: return;
1659: }
1660:
1661: while (len > 0) {
1662: if (pos >= MAX_BLOCK_SIZE) {
1663: drain();
1664: }
1665: if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1666: // avoid unnecessary copy
1667: writeBlockHeader(MAX_BLOCK_SIZE);
1668: out.write(b, off, MAX_BLOCK_SIZE);
1669: off += MAX_BLOCK_SIZE;
1670: len -= MAX_BLOCK_SIZE;
1671: } else {
1672: int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1673: System.arraycopy(b, off, buf, pos, wlen);
1674: pos += wlen;
1675: off += wlen;
1676: len -= wlen;
1677: }
1678: }
1679: }
1680:
1681: /**
1682: * Writes all buffered data from this stream to the underlying stream,
1683: * but does not flush underlying stream.
1684: */
1685: void drain() throws IOException {
1686: if (pos == 0) {
1687: return;
1688: }
1689: if (blkmode) {
1690: writeBlockHeader(pos);
1691: }
1692: out.write(buf, 0, pos);
1693: pos = 0;
1694: }
1695:
1696: /**
1697: * Writes block data header. Data blocks shorter than 256 bytes are
1698: * prefixed with a 2-byte header; all others start with a 5-byte
1699: * header.
1700: */
1701: private void writeBlockHeader(int len) throws IOException {
1702: if (len <= 0xFF) {
1703: hbuf[0] = TC_BLOCKDATA;
1704: hbuf[1] = (byte) len;
1705: out.write(hbuf, 0, 2);
1706: } else {
1707: hbuf[0] = TC_BLOCKDATALONG;
1708: Bits.putInt(hbuf, 1, len);
1709: out.write(hbuf, 0, 5);
1710: }
1711: }
1712:
1713: /* ----------------- primitive data output methods ----------------- */
1714: /*
1715: * The following methods are equivalent to their counterparts in
1716: * DataOutputStream, except that they partition written data into data
1717: * blocks when in block data mode.
1718: */
1719:
1720: public void writeBoolean(boolean v) throws IOException {
1721: if (pos >= MAX_BLOCK_SIZE) {
1722: drain();
1723: }
1724: Bits.putBoolean(buf, pos++, v);
1725: }
1726:
1727: public void writeByte(int v) throws IOException {
1728: if (pos >= MAX_BLOCK_SIZE) {
1729: drain();
1730: }
1731: buf[pos++] = (byte) v;
1732: }
1733:
1734: public void writeChar(int v) throws IOException {
1735: if (pos + 2 <= MAX_BLOCK_SIZE) {
1736: Bits.putChar(buf, pos, (char) v);
1737: pos += 2;
1738: } else {
1739: dout.writeChar(v);
1740: }
1741: }
1742:
1743: public void writeShort(int v) throws IOException {
1744: if (pos + 2 <= MAX_BLOCK_SIZE) {
1745: Bits.putShort(buf, pos, (short) v);
1746: pos += 2;
1747: } else {
1748: dout.writeShort(v);
1749: }
1750: }
1751:
1752: public void writeInt(int v) throws IOException {
1753: if (pos + 4 <= MAX_BLOCK_SIZE) {
1754: Bits.putInt(buf, pos, v);
1755: pos += 4;
1756: } else {
1757: dout.writeInt(v);
1758: }
1759: }
1760:
1761: public void writeFloat(float v) throws IOException {
1762: if (pos + 4 <= MAX_BLOCK_SIZE) {
1763: Bits.putFloat(buf, pos, v);
1764: pos += 4;
1765: } else {
1766: dout.writeFloat(v);
1767: }
1768: }
1769:
1770: public void writeLong(long v) throws IOException {
1771: if (pos + 8 <= MAX_BLOCK_SIZE) {
1772: Bits.putLong(buf, pos, v);
1773: pos += 8;
1774: } else {
1775: dout.writeLong(v);
1776: }
1777: }
1778:
1779: public void writeDouble(double v) throws IOException {
1780: if (pos + 8 <= MAX_BLOCK_SIZE) {
1781: Bits.putDouble(buf, pos, v);
1782: pos += 8;
1783: } else {
1784: dout.writeDouble(v);
1785: }
1786: }
1787:
1788: public void writeBytes(String s) throws IOException {
1789: int endoff = s.length();
1790: int cpos = 0;
1791: int csize = 0;
1792: for (int off = 0; off < endoff;) {
1793: if (cpos >= csize) {
1794: cpos = 0;
1795: csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1796: s.getChars(off, off + csize, cbuf, 0);
1797: }
1798: if (pos >= MAX_BLOCK_SIZE) {
1799: drain();
1800: }
1801: int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1802: int stop = pos + n;
1803: while (pos < stop) {
1804: buf[pos++] = (byte) cbuf[cpos++];
1805: }
1806: off += n;
1807: }
1808: }
1809:
1810: public void writeChars(String s) throws IOException {
1811: int endoff = s.length();
1812: for (int off = 0; off < endoff;) {
1813: int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1814: s.getChars(off, off + csize, cbuf, 0);
1815: writeChars(cbuf, 0, csize);
1816: off += csize;
1817: }
1818: }
1819:
1820: public void writeUTF(String s) throws IOException {
1821: writeUTF(s, getUTFLength(s));
1822: }
1823:
1824: /* -------------- primitive data array output methods -------------- */
1825: /*
1826: * The following methods write out spans of primitive data values.
1827: * Though equivalent to calling the corresponding primitive write
1828: * methods repeatedly, these methods are optimized for writing groups
1829: * of primitive data values more efficiently.
1830: */
1831:
1832: void writeBooleans(boolean[] v, int off, int len)
1833: throws IOException {
1834: int endoff = off + len;
1835: while (off < endoff) {
1836: if (pos >= MAX_BLOCK_SIZE) {
1837: drain();
1838: }
1839: int stop = Math.min(endoff, off
1840: + (MAX_BLOCK_SIZE - pos));
1841: while (off < stop) {
1842: Bits.putBoolean(buf, pos++, v[off++]);
1843: }
1844: }
1845: }
1846:
1847: void writeChars(char[] v, int off, int len) throws IOException {
1848: int limit = MAX_BLOCK_SIZE - 2;
1849: int endoff = off + len;
1850: while (off < endoff) {
1851: if (pos <= limit) {
1852: int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1853: int stop = Math.min(endoff, off + avail);
1854: while (off < stop) {
1855: Bits.putChar(buf, pos, v[off++]);
1856: pos += 2;
1857: }
1858: } else {
1859: dout.writeChar(v[off++]);
1860: }
1861: }
1862: }
1863:
1864: void writeShorts(short[] v, int off, int len)
1865: throws IOException {
1866: int limit = MAX_BLOCK_SIZE - 2;
1867: int endoff = off + len;
1868: while (off < endoff) {
1869: if (pos <= limit) {
1870: int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1871: int stop = Math.min(endoff, off + avail);
1872: while (off < stop) {
1873: Bits.putShort(buf, pos, v[off++]);
1874: pos += 2;
1875: }
1876: } else {
1877: dout.writeShort(v[off++]);
1878: }
1879: }
1880: }
1881:
1882: void writeInts(int[] v, int off, int len) throws IOException {
1883: int limit = MAX_BLOCK_SIZE - 4;
1884: int endoff = off + len;
1885: while (off < endoff) {
1886: if (pos <= limit) {
1887: int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1888: int stop = Math.min(endoff, off + avail);
1889: while (off < stop) {
1890: Bits.putInt(buf, pos, v[off++]);
1891: pos += 4;
1892: }
1893: } else {
1894: dout.writeInt(v[off++]);
1895: }
1896: }
1897: }
1898:
1899: void writeFloats(float[] v, int off, int len)
1900: throws IOException {
1901: int limit = MAX_BLOCK_SIZE - 4;
1902: int endoff = off + len;
1903: while (off < endoff) {
1904: if (pos <= limit) {
1905: int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1906: int chunklen = Math.min(endoff - off, avail);
1907: floatsToBytes(v, off, buf, pos, chunklen);
1908: off += chunklen;
1909: pos += chunklen << 2;
1910: } else {
1911: dout.writeFloat(v[off++]);
1912: }
1913: }
1914: }
1915:
1916: void writeLongs(long[] v, int off, int len) throws IOException {
1917: int limit = MAX_BLOCK_SIZE - 8;
1918: int endoff = off + len;
1919: while (off < endoff) {
1920: if (pos <= limit) {
1921: int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1922: int stop = Math.min(endoff, off + avail);
1923: while (off < stop) {
1924: Bits.putLong(buf, pos, v[off++]);
1925: pos += 8;
1926: }
1927: } else {
1928: dout.writeLong(v[off++]);
1929: }
1930: }
1931: }
1932:
1933: void writeDoubles(double[] v, int off, int len)
1934: throws IOException {
1935: int limit = MAX_BLOCK_SIZE - 8;
1936: int endoff = off + len;
1937: while (off < endoff) {
1938: if (pos <= limit) {
1939: int avail = (MAX_BLOCK_SIZE - pos) >> 3;
1940: int chunklen = Math.min(endoff - off, avail);
1941: doublesToBytes(v, off, buf, pos, chunklen);
1942: off += chunklen;
1943: pos += chunklen << 3;
1944: } else {
1945: dout.writeDouble(v[off++]);
1946: }
1947: }
1948: }
1949:
1950: /**
1951: * Returns the length in bytes of the UTF encoding of the given string.
1952: */
1953: long getUTFLength(String s) {
1954: int len = s.length();
1955: long utflen = 0;
1956: for (int off = 0; off < len;) {
1957: int csize = Math.min(len - off, CHAR_BUF_SIZE);
1958: s.getChars(off, off + csize, cbuf, 0);
1959: for (int cpos = 0; cpos < csize; cpos++) {
1960: char c = cbuf[cpos];
1961: if (c >= 0x0001 && c <= 0x007F) {
1962: utflen++;
1963: } else if (c > 0x07FF) {
1964: utflen += 3;
1965: } else {
1966: utflen += 2;
1967: }
1968: }
1969: off += csize;
1970: }
1971: return utflen;
1972: }
1973:
1974: /**
1975: * Writes the given string in UTF format. This method is used in
1976: * situations where the UTF encoding length of the string is already
1977: * known; specifying it explicitly avoids a prescan of the string to
1978: * determine its UTF length.
1979: */
1980: void writeUTF(String s, long utflen) throws IOException {
1981: if (utflen > 0xFFFFL) {
1982: throw new UTFDataFormatException();
1983: }
1984: writeShort((int) utflen);
1985: if (utflen == (long) s.length()) {
1986: writeBytes(s);
1987: } else {
1988: writeUTFBody(s);
1989: }
1990: }
1991:
1992: /**
1993: * Writes given string in "long" UTF format. "Long" UTF format is
1994: * identical to standard UTF, except that it uses an 8 byte header
1995: * (instead of the standard 2 bytes) to convey the UTF encoding length.
1996: */
1997: void writeLongUTF(String s) throws IOException {
1998: writeLongUTF(s, getUTFLength(s));
1999: }
2000:
2001: /**
2002: * Writes given string in "long" UTF format, where the UTF encoding
2003: * length of the string is already known.
2004: */
2005: void writeLongUTF(String s, long utflen) throws IOException {
2006: writeLong(utflen);
2007: if (utflen == (long) s.length()) {
2008: writeBytes(s);
2009: } else {
2010: writeUTFBody(s);
2011: }
2012: }
2013:
2014: /**
2015: * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2016: * 8-byte length header) of the UTF encoding for the given string.
2017: */
2018: private void writeUTFBody(String s) throws IOException {
2019: int limit = MAX_BLOCK_SIZE - 3;
2020: int len = s.length();
2021: for (int off = 0; off < len;) {
2022: int csize = Math.min(len - off, CHAR_BUF_SIZE);
2023: s.getChars(off, off + csize, cbuf, 0);
2024: for (int cpos = 0; cpos < csize; cpos++) {
2025: char c = cbuf[cpos];
2026: if (pos <= limit) {
2027: if (c <= 0x007F && c != 0) {
2028: buf[pos++] = (byte) c;
2029: } else if (c > 0x07FF) {
2030: buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2031: buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2032: buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2033: pos += 3;
2034: } else {
2035: buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2036: buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2037: pos += 2;
2038: }
2039: } else { // write one byte at a time to normalize block
2040: if (c <= 0x007F && c != 0) {
2041: write(c);
2042: } else if (c > 0x07FF) {
2043: write(0xE0 | ((c >> 12) & 0x0F));
2044: write(0x80 | ((c >> 6) & 0x3F));
2045: write(0x80 | ((c >> 0) & 0x3F));
2046: } else {
2047: write(0xC0 | ((c >> 6) & 0x1F));
2048: write(0x80 | ((c >> 0) & 0x3F));
2049: }
2050: }
2051: }
2052: off += csize;
2053: }
2054: }
2055: }
2056:
2057: /**
2058: * Lightweight identity hash table which maps objects to integer handles,
2059: * assigned in ascending order.
2060: */
2061: private static class HandleTable {
2062:
2063: /* number of mappings in table/next available handle */
2064: private int size;
2065: /* size threshold determining when to expand hash spine */
2066: private int threshold;
2067: /* factor for computing size threshold */
2068: private final float loadFactor;
2069: /* maps hash value -> candidate handle value */
2070: private int[] spine;
2071: /* maps handle value -> next candidate handle value */
2072: private int[] next;
2073: /* maps handle value -> associated object */
2074: private Object[] objs;
2075:
2076: /**
2077: * Creates new HandleTable with given capacity and load factor.
2078: */
2079: HandleTable(int initialCapacity, float loadFactor) {
2080: this .loadFactor = loadFactor;
2081: spine = new int[initialCapacity];
2082: next = new int[initialCapacity];
2083: objs = new Object[initialCapacity];
2084: threshold = (int) (initialCapacity * loadFactor);
2085: clear();
2086: }
2087:
2088: /**
2089: * Assigns next available handle to given object, and returns handle
2090: * value. Handles are assigned in ascending order starting at 0.
2091: */
2092: int assign(Object obj) {
2093: if (size >= next.length) {
2094: growEntries();
2095: }
2096: if (size >= threshold) {
2097: growSpine();
2098: }
2099: insert(obj, size);
2100: return size++;
2101: }
2102:
2103: /**
2104: * Looks up and returns handle associated with given object, or -1 if
2105: * no mapping found.
2106: */
2107: int lookup(Object obj) {
2108: if (size == 0) {
2109: return -1;
2110: }
2111: int index = hash(obj) % spine.length;
2112: for (int i = spine[index]; i >= 0; i = next[i]) {
2113: if (objs[i] == obj) {
2114: return i;
2115: }
2116: }
2117: return -1;
2118: }
2119:
2120: /**
2121: * Resets table to its initial (empty) state.
2122: */
2123: void clear() {
2124: Arrays.fill(spine, -1);
2125: Arrays.fill(objs, 0, size, null);
2126: size = 0;
2127: }
2128:
2129: /**
2130: * Returns the number of mappings currently in table.
2131: */
2132: int size() {
2133: return size;
2134: }
2135:
2136: /**
2137: * Inserts mapping object -> handle mapping into table. Assumes table
2138: * is large enough to accomodate new mapping.
2139: */
2140: private void insert(Object obj, int handle) {
2141: int index = hash(obj) % spine.length;
2142: objs[handle] = obj;
2143: next[handle] = spine[index];
2144: spine[index] = handle;
2145: }
2146:
2147: /**
2148: * Expands the hash "spine" -- equivalent to increasing the number of
2149: * buckets in a conventional hash table.
2150: */
2151: private void growSpine() {
2152: spine = new int[(spine.length << 1) + 1];
2153: threshold = (int) (spine.length * loadFactor);
2154: Arrays.fill(spine, -1);
2155: for (int i = 0; i < size; i++) {
2156: insert(objs[i], i);
2157: }
2158: }
2159:
2160: /**
2161: * Increases hash table capacity by lengthening entry arrays.
2162: */
2163: private void growEntries() {
2164: int newLength = (next.length << 1) + 1;
2165: int[] newNext = new int[newLength];
2166: /* IAI - 15 */
2167: CVM.copyIntArray(next, 0, newNext, 0, size);
2168: /* IAI - 15 */
2169: next = newNext;
2170:
2171: Object[] newObjs = new Object[newLength];
2172: /* IAI - 15 */
2173: CVM.copyObjectArray(objs, 0, newObjs, 0, size);
2174: /* IAI - 15 */
2175: objs = newObjs;
2176: }
2177:
2178: /**
2179: * Returns hash value for given object.
2180: */
2181: private int hash(Object obj) {
2182: return System.identityHashCode(obj) & 0x7FFFFFFF;
2183: }
2184: }
2185:
2186: /**
2187: * Lightweight identity hash table which maps objects to replacement
2188: * objects.
2189: */
2190: private static class ReplaceTable {
2191:
2192: /* maps object -> index */
2193: private final HandleTable htab;
2194: /* maps index -> replacement object */
2195: private Object[] reps;
2196:
2197: /**
2198: * Creates new ReplaceTable with given capacity and load factor.
2199: */
2200: ReplaceTable(int initialCapacity, float loadFactor) {
2201: htab = new HandleTable(initialCapacity, loadFactor);
2202: reps = new Object[initialCapacity];
2203: }
2204:
2205: /**
2206: * Enters mapping from object to replacement object.
2207: */
2208: void assign(Object obj, Object rep) {
2209: int index = htab.assign(obj);
2210: while (index >= reps.length) {
2211: grow();
2212: }
2213: reps[index] = rep;
2214: }
2215:
2216: /**
2217: * Looks up and returns replacement for given object. If no
2218: * replacement is found, returns the lookup object itself.
2219: */
2220: Object lookup(Object obj) {
2221: int index = htab.lookup(obj);
2222: return (index >= 0) ? reps[index] : obj;
2223: }
2224:
2225: /**
2226: * Resets table to its initial (empty) state.
2227: */
2228: void clear() {
2229: Arrays.fill(reps, 0, htab.size(), null);
2230: htab.clear();
2231: }
2232:
2233: /**
2234: * Returns the number of mappings currently in table.
2235: */
2236: int size() {
2237: return htab.size();
2238: }
2239:
2240: /**
2241: * Increases table capacity.
2242: */
2243: private void grow() {
2244: Object[] newReps = new Object[(reps.length << 1) + 1];
2245: /* IAI - 15 */
2246: CVM.copyObjectArray(reps, 0, newReps, 0, reps.length);
2247: /* IAI - 15 */
2248: reps = newReps;
2249: }
2250: }
2251: }
|