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