0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package java.io;
0019:
0020: import java.lang.reflect.InvocationTargetException;
0021: import java.lang.reflect.Method;
0022: import java.lang.reflect.Proxy;
0023: import java.util.IdentityHashMap;
0024:
0025: import org.apache.harmony.luni.util.Msg;
0026:
0027: /**
0028: * An ObjectOutputStream can be used to save Java objects into a stream where
0029: * the objects can be loaded later with an ObjectInputStream. Primitive data
0030: * (ints, bytes, chars, etc) can also be saved.
0031: *
0032: * @see ObjectInputStream
0033: * @see ObjectOutput
0034: * @see Serializable
0035: * @see Externalizable
0036: */
0037: public class ObjectOutputStream extends OutputStream implements
0038: ObjectOutput, ObjectStreamConstants {
0039:
0040: /*
0041: * Mask to zero SC_BLOC_DATA bit.
0042: */
0043: private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF);
0044:
0045: /*
0046: * How many nested levels to writeObject. We may not need this.
0047: */
0048: private int nestedLevels;
0049:
0050: /*
0051: * Where we write to
0052: */
0053: private DataOutputStream output;
0054:
0055: /*
0056: * If object replacement is enabled or not
0057: */
0058: private boolean enableReplace;
0059:
0060: /*
0061: * Where we write primitive types to
0062: */
0063: private DataOutputStream primitiveTypes;
0064:
0065: /*
0066: * Where the write primitive types are actually written to
0067: */
0068: private ByteArrayOutputStream primitiveTypesBuffer;
0069:
0070: /*
0071: * Table mapping Object -> Integer (handle)
0072: */
0073: private IdentityHashMap<Object, Integer> objectsWritten;
0074:
0075: /*
0076: * All objects are assigned an ID (integer handle)
0077: */
0078: private int currentHandle;
0079:
0080: /*
0081: * Used by defaultWriteObject
0082: */
0083: private Object currentObject;
0084:
0085: /*
0086: * Used by defaultWriteObject
0087: */
0088: private ObjectStreamClass currentClass;
0089:
0090: /*
0091: * Either ObjectStreamConstants.PROTOCOL_VERSION_1 or
0092: * ObjectStreamConstants.PROTOCOL_VERSION_2
0093: */
0094: private int protocolVersion;
0095:
0096: /*
0097: * Used to detect nested exception when saving an exception due to an error
0098: */
0099: private StreamCorruptedException nestedException;
0100:
0101: /*
0102: * Used to keep track of the PutField object for the class/object being
0103: * written
0104: */
0105: private EmulatedFieldsForDumping currentPutField;
0106:
0107: /*
0108: * Allows the receiver to decide if it needs to call writeObjectOverride
0109: */
0110: private boolean subclassOverridingImplementation;
0111:
0112: /**
0113: * Inner class to provide access to serializable fields
0114: */
0115: public static abstract class PutField {
0116: public abstract void put(String name, boolean value);
0117:
0118: public abstract void put(String name, char value);
0119:
0120: public abstract void put(String name, byte value);
0121:
0122: public abstract void put(String name, short value);
0123:
0124: public abstract void put(String name, int value);
0125:
0126: public abstract void put(String name, long value);
0127:
0128: public abstract void put(String name, float value);
0129:
0130: public abstract void put(String name, double value);
0131:
0132: public abstract void put(String name, Object value);
0133:
0134: /**
0135: * @deprecated This method is unsafe and may corrupt the output stream.
0136: * Use ObjectOutputStream#writeFields() instead.
0137: */
0138: @Deprecated
0139: public abstract void write(ObjectOutput out) throws IOException;
0140: }
0141:
0142: /**
0143: * Constructs a new <code>ObjectOutputStream</code>. The representation
0144: * and proper initialization is in the hands of subclasses.
0145: *
0146: * @throws IOException
0147: * @throws SecurityException
0148: * if subclassing this is not allowed
0149: *
0150: * @see SecurityManager#checkPermission(java.security.Permission)
0151: */
0152: protected ObjectOutputStream() throws IOException,
0153: SecurityException {
0154: super ();
0155: SecurityManager currentManager = System.getSecurityManager();
0156: if (currentManager != null) {
0157: currentManager
0158: .checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
0159: }
0160: /*
0161: * WARNING - we should throw IOException if not called from a subclass
0162: * according to the JavaDoc. Add the test.
0163: */
0164: this .subclassOverridingImplementation = true;
0165: }
0166:
0167: /**
0168: * Constructs a new ObjectOutputStream on the OutputStream
0169: * <code>output</code>. All writes are now filtered through this stream.
0170: *
0171: * @param output
0172: * The non-null OutputStream to filter writes on.
0173: *
0174: * @throws IOException
0175: * If an IO exception happened when writing the object stream
0176: * header
0177: */
0178: public ObjectOutputStream(OutputStream output) throws IOException {
0179: Class<?> implementationClass = getClass();
0180: Class<?> this Class = ObjectOutputStream.class;
0181: if (implementationClass != this Class) {
0182: boolean mustCheck = false;
0183: try {
0184: Method method = implementationClass
0185: .getMethod(
0186: "putFields", //$NON-NLS-1$
0187: ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
0188: mustCheck = method.getDeclaringClass() != this Class;
0189: } catch (NoSuchMethodException e) {
0190: }
0191: if (!mustCheck) {
0192: try {
0193: Method method = implementationClass.getMethod(
0194: "writeUnshared", //$NON-NLS-1$
0195: ObjectStreamClass.UNSHARED_PARAM_TYPES);
0196: mustCheck = method.getDeclaringClass() != this Class;
0197: } catch (NoSuchMethodException e) {
0198: }
0199: }
0200: if (mustCheck) {
0201: SecurityManager sm = System.getSecurityManager();
0202: if (sm != null) {
0203: sm
0204: .checkPermission(ObjectStreamConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
0205: }
0206: }
0207: }
0208: this .output = (output instanceof DataOutputStream) ? (DataOutputStream) output
0209: : new DataOutputStream(output);
0210: this .enableReplace = false;
0211: this .protocolVersion = PROTOCOL_VERSION_2;
0212: this .subclassOverridingImplementation = false;
0213:
0214: resetState();
0215: this .nestedException = new StreamCorruptedException();
0216: // So write...() methods can be used by
0217: // subclasses during writeStreamHeader()
0218: primitiveTypes = this .output;
0219: // Has to be done here according to the specification
0220: writeStreamHeader();
0221: primitiveTypes = null;
0222: }
0223:
0224: /**
0225: * Writes optional information for class <code>aClass</code> into the
0226: * stream represented by the receiver. This optional data can be read when
0227: * deserializing the class descriptor (ObjectStreamClass) for this class
0228: * from the input stream. By default no extra data is saved.
0229: *
0230: * @param aClass
0231: * The class to annotate
0232: *
0233: * @throws IOException
0234: * If an IO exception happened when annotating the class.
0235: *
0236: * @see ObjectInputStream#resolveClass
0237: */
0238: protected void annotateClass(Class<?> aClass) throws IOException {
0239: // By default no extra info is saved. Subclasses can override
0240: }
0241:
0242: /**
0243: * Writes optional information for a proxy class into the stream represented
0244: * by the receiver. This optional data can be read when deserializing the
0245: * proxy class from the input stream. By default no extra data is saved.
0246: *
0247: * @param aClass
0248: * The proxy class to annotate
0249: *
0250: * @throws IOException
0251: * If an IO exception happened when annotating the class.
0252: *
0253: * @see ObjectInputStream#resolveProxyClass
0254: */
0255: protected void annotateProxyClass(Class<?> aClass)
0256: throws IOException {
0257: // By default no extra info is saved. Subclasses can override
0258: }
0259:
0260: /**
0261: * Do the necessary work to see if the receiver can be used to write
0262: * primitive types like int, char, etc.
0263: */
0264: private void checkWritePrimitiveTypes() {
0265: if (primitiveTypes == null) {
0266: // If we got here we have no Stream previously created
0267: // WARNING - if the stream does not grow, this code is wrong
0268: primitiveTypesBuffer = new ByteArrayOutputStream(128);
0269: primitiveTypes = new DataOutputStream(primitiveTypesBuffer);
0270: }
0271: }
0272:
0273: /**
0274: * Close this ObjectOutputStream. Any buffered data is flushed. This
0275: * implementation closes the target stream.
0276: *
0277: * @throws IOException
0278: * If an error occurs attempting to close this stream.
0279: */
0280: @Override
0281: public void close() throws IOException {
0282: // First flush what is needed (primitive data, etc)
0283: flush();
0284: output.close();
0285: }
0286:
0287: /**
0288: * Computes the collection of emulated fields that users can manipulate to
0289: * store a representation different than the one declared by the class of
0290: * the object being dumped.
0291: *
0292: * @see #writeFields
0293: * @see #writeFieldValues(EmulatedFieldsForDumping)
0294: */
0295: private void computePutField() {
0296: currentPutField = new EmulatedFieldsForDumping(currentClass);
0297: }
0298:
0299: /**
0300: * Default method to write objects into the receiver. Fields defined in the
0301: * object's class and superclasses (which are Serializable) will be saved.
0302: *
0303: * @throws IOException
0304: * If an IO error occurs attempting to write the object data
0305: *
0306: * @see ObjectInputStream#defaultReadObject
0307: */
0308: public void defaultWriteObject() throws IOException {
0309: // We can't be called from just anywhere. There are rules.
0310: if (currentObject == null) {
0311: throw new NotActiveException();
0312: }
0313: writeFieldValues(currentObject, currentClass);
0314: }
0315:
0316: /**
0317: * Flushes buffered primitive data into the receiver.
0318: *
0319: * @throws IOException
0320: * If an error occurs attempting to drain the data
0321: */
0322: protected void drain() throws IOException {
0323: if (primitiveTypes == null || primitiveTypesBuffer == null) {
0324: return;
0325: }
0326:
0327: // If we got here we have a Stream previously created
0328: int offset = 0;
0329: byte[] written = primitiveTypesBuffer.toByteArray();
0330: // Normalize the primitive data
0331: while (offset < written.length) {
0332: int toWrite = written.length - offset > 1024 ? 1024
0333: : written.length - offset;
0334: if (toWrite < 256) {
0335: output.writeByte(TC_BLOCKDATA);
0336: output.writeByte((byte) toWrite);
0337: } else {
0338: output.writeByte(TC_BLOCKDATALONG);
0339: output.writeInt(toWrite);
0340: }
0341:
0342: // write primitive types we had and the marker of end-of-buffer
0343: output.write(written, offset, toWrite);
0344: offset += toWrite;
0345: }
0346:
0347: // and now we're clean to a state where we can write an object
0348: primitiveTypes = null;
0349: primitiveTypesBuffer = null;
0350: }
0351:
0352: /**
0353: * Dumps the parameter <code>obj</code> only if it is <code>null</code>
0354: * or an object that has already been dumped previously.
0355: *
0356: * @param obj
0357: * Object to check if an instance previously dumped by this
0358: * stream.
0359: * @return null if it is an instance which has not been dumped yet (and this
0360: * method does nothing). Integer, if <code>obj</code> is an
0361: * instance which has been dumped already. In this case this method
0362: * saves the cyclic reference.
0363: *
0364: * @throws IOException
0365: * If an error occurs attempting to save <code>null</code> or
0366: * a cyclic reference.
0367: */
0368: private Integer dumpCycle(Object obj) throws IOException {
0369: // If the object has been saved already, save its handle only
0370: Integer handle = registeredObjectHandleFor(obj);
0371: if (handle != null) {
0372: writeCyclicReference(handle);
0373: return handle;
0374: }
0375: return null;
0376: }
0377:
0378: /**
0379: * Enables/disables object replacement for the receiver. By default this is
0380: * not enabled. Only trusted subclasses (loaded with system class loader)
0381: * can override this behavior.
0382: *
0383: * @param enable
0384: * if true, enables replacement. If false, disables replacement.
0385: * @return boolean the previous configuration (if it was enabled or
0386: * disabled)
0387: *
0388: * @throws SecurityException
0389: * If the class of the receiver is not trusted
0390: *
0391: * @see #replaceObject
0392: * @see ObjectInputStream#enableResolveObject
0393: */
0394: protected boolean enableReplaceObject(boolean enable)
0395: throws SecurityException {
0396: if (enable) {
0397: // The Stream has to be trusted for this feature to be enabled.
0398: // trusted means the stream's classloader has to be null
0399: SecurityManager currentManager = System
0400: .getSecurityManager();
0401: if (currentManager != null) {
0402: currentManager.checkPermission(SUBSTITUTION_PERMISSION);
0403: }
0404: }
0405: boolean originalValue = enableReplace;
0406: enableReplace = enable;
0407: return originalValue;
0408: }
0409:
0410: /**
0411: * Flush this ObjectOutputStream. Any pending writes to the underlying
0412: * stream are written out when this method is invoked.
0413: *
0414: * @throws IOException
0415: * If an error occurs attempting to flush this
0416: * ObjectOutputStream.
0417: */
0418: @Override
0419: public void flush() throws IOException {
0420: drain();
0421: output.flush();
0422: }
0423:
0424: /**
0425: * Get the value of field named
0426: * <code>fieldName<code> of object <code>instance</code>. The
0427: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0428: * a boolean.
0429: *
0430: * This method could be implemented non-natively on top of java.lang.reflect implementations
0431: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0432: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0433: * by the use of a native method like this one.
0434: *
0435: * @param instance Object whose field value we want to fetch
0436: * @param declaringClass The class that declares the field
0437: * @param fieldName Name of the field we want to fetch
0438: * @return the value of the field
0439: *
0440: * @throws NoSuchFieldError If the field does not exist.
0441: */
0442: private static native boolean getFieldBool(Object instance,
0443: Class<?> declaringClass, String fieldName);
0444:
0445: /**
0446: * Get the value of field named
0447: * <code>fieldName<code> of object <code>instance</code>. The
0448: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0449: * a byte
0450: *
0451: * This method could be implemented non-natively on top of java.lang.reflect implementations
0452: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0453: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0454: * by the use of a native method like this one.
0455: *
0456: * @param instance Object whose field value we want to fetch
0457: * @param declaringClass The class that declares the field
0458: * @param fieldName Name of the field we want to fetch
0459: * @return the value of the field
0460: *
0461: * @throws NoSuchFieldError If the field does not exist.
0462: */
0463: private static native byte getFieldByte(Object instance,
0464: Class<?> declaringClass, String fieldName);
0465:
0466: /**
0467: * Get the value of field named
0468: * <code>fieldName<code> of object <code>instance</code>. The
0469: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0470: * a char.
0471: *
0472: * This method could be implemented non-natively on top of java.lang.reflect implementations
0473: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0474: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0475: * by the use of a native method like this one.
0476: *
0477: * @param instance Object whose field value we want to fetch
0478: * @param declaringClass The class that declares the field
0479: * @param fieldName Name of the field we want to fetch
0480: * @return the value of the field
0481: *
0482: * @throws NoSuchFieldError If the field does not exist.
0483: */
0484: private static native char getFieldChar(Object instance,
0485: Class<?> declaringClass, String fieldName);
0486:
0487: /**
0488: * Get the value of field named
0489: * <code>fieldName<code> of object <code>instance</code>. The
0490: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0491: * a double.
0492: *
0493: * This method could be implemented non-natively on top of java.lang.reflect implementations
0494: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0495: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0496: * by the use of a native method like this one.
0497: *
0498: * @param instance Object whose field value we want to fetch
0499: * @param declaringClass The class that declares the field
0500: * @param fieldName Name of the field we want to fetch
0501: * @return the value of the field
0502: *
0503: * @throws NoSuchFieldError If the field does not exist.
0504: */
0505: private static native double getFieldDouble(Object instance,
0506: Class<?> declaringClass, String fieldName);
0507:
0508: /**
0509: * Get the value of field named
0510: * <code>fieldName<code> of object <code>instance</code>. The
0511: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0512: * a float.
0513: *
0514: * This method could be implemented non-natively on top of java.lang.reflect implementations
0515: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0516: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0517: * by the use of a native method like this one.
0518: *
0519: * @param instance Object whose field value we want to fetch
0520: * @param declaringClass The class that declares the field
0521: * @param fieldName Name of the field we want to fetch
0522: * @return the value of the field
0523: *
0524: * @throws NoSuchFieldError If the field does not exist.
0525: */
0526: private static native float getFieldFloat(Object instance,
0527: Class<?> declaringClass, String fieldName);
0528:
0529: /**
0530: * Get the value of field named
0531: * <code>fieldName<code> of object <code>instance</code>. The
0532: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0533: * an int.
0534: *
0535: * This method could be implemented non-natively on top of java.lang.reflect implementations
0536: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0537: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0538: * by the use of a native method like this one.
0539: *
0540: * @param instance Object whose field value we want to fetch
0541: * @param declaringClass The class that declares the field
0542: * @param fieldName Name of the field we want to fetch
0543: * @return the value of the field
0544: *
0545: * @throws NoSuchFieldError If the field does not exist.
0546: */
0547: private static native int getFieldInt(Object instance,
0548: Class<?> declaringClass, String fieldName);
0549:
0550: /**
0551: * Get the value of field named
0552: * <code>fieldName<code> of object <code>instance</code>. The
0553: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0554: * a long.
0555: *
0556: * This method could be implemented non-natively on top of java.lang.reflect implementations
0557: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0558: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0559: * by the use of a native method like this one.
0560: *
0561: * @param instance Object whose field value we want to fetch
0562: * @param declaringClass The class that declares the field
0563: * @param fieldName Name of the field we want to fetch
0564: * @return the value of the field
0565: *
0566: * @throws NoSuchFieldError If the field does not exist.
0567: */
0568: private static native long getFieldLong(Object instance,
0569: Class<?> declaringClass, String fieldName);
0570:
0571: /**
0572: * Get the value of field named
0573: * <code>fieldName<code> of object <code>instance</code>. The
0574: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0575: * an Object type whose name is <code>fieldTypeName</code>.
0576: *
0577: * This method could be implemented non-natively on top of java.lang.reflect implementations
0578: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0579: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0580: * by the use of a native method like this one.
0581: *
0582: * @param instance Object whose field value we want to fetch
0583: * @param declaringClass The class that declares the field
0584: * @param fieldName Name of the field we want to fetch
0585: * @param fieldTypeName Name of the class that defines the type of this field
0586: * @return the value of the field
0587: *
0588: * @throws NoSuchFieldError If the field does not exist.
0589: */
0590: private static native Object getFieldObj(Object instance,
0591: Class<?> declaringClass, String fieldName,
0592: String fieldTypeName);
0593:
0594: /**
0595: * Get the value of field named
0596: * <code>fieldName<code> of object <code>instance</code>. The
0597: * field is declared by class <code>declaringClass</code>. The field is supposed to be
0598: * a short.
0599: *
0600: * This method could be implemented non-natively on top of java.lang.reflect implementations
0601: * that support the <code>setAccessible</code> API, at the expense of extra object creation
0602: * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except
0603: * by the use of a native method like this one.
0604: *
0605: * @param instance Object whose field value we want to fetch
0606: * @param declaringClass The class that declares the field
0607: * @param fieldName Name of the field we want to fetch
0608: * @return the value of the field
0609: *
0610: * @throws NoSuchFieldError If the field does not exist.
0611: */
0612: private static native short getFieldShort(Object instance,
0613: Class<?> declaringClass, String fieldName);
0614:
0615: /**
0616: * Return the next <code>int</code> handle to be used to indicate cyclic
0617: * references being saved to the stream.
0618: *
0619: * @return int, the next handle to represent the next cyclic reference
0620: */
0621: private int nextHandle() {
0622: return this .currentHandle++;
0623: }
0624:
0625: /**
0626: * Return the <code>PutField</code> object for the receiver. This allows
0627: * users to transfer values from actual object fields in the object being
0628: * dumped to the emulated fields represented by the <code>PutField</code>
0629: * returned by this method.
0630: *
0631: * @return the PutFieldObject for the receiver
0632: *
0633: * @throws IOException
0634: * If an IO error occurs
0635: * @throws NotActiveException
0636: * If this method is not called from writeObject()
0637: *
0638: * @see ObjectInputStream#defaultReadObject
0639: */
0640: public PutField putFields() throws IOException {
0641: // We can't be called from just anywhere. There are rules.
0642: if (currentObject == null) {
0643: throw new NotActiveException();
0644: }
0645: if (currentPutField == null) {
0646: computePutField();
0647: }
0648: return currentPutField;
0649: }
0650:
0651: /**
0652: * Return the <code>Integer</code> handle used to tag object
0653: * <code>obj</code> as an instance that has been dumped already. Return
0654: * <code>null</code> if object <code>obj</code> has not been saved yet.
0655: *
0656: * @param obj
0657: * the object
0658: * @return null if object <code>obj</code> has not been saved yet. Integer
0659: * The handle that this object was assigned when it was saved.
0660: */
0661: private Integer registeredObjectHandleFor(Object obj) {
0662: return objectsWritten.get(obj);
0663: }
0664:
0665: /**
0666: * Assume object <code>obj</code> has not been dumped yet, and assign a
0667: * handle to it
0668: *
0669: * @param obj
0670: * Non-null object being dumped.
0671: * @return the handle that this object is being assigned.
0672: *
0673: * @see #nextHandle
0674: */
0675: private Integer registerObjectWritten(Object obj) {
0676: Integer handle = Integer.valueOf(nextHandle());
0677: registerObjectWritten(obj, handle);
0678: return handle;
0679: }
0680:
0681: /**
0682: * Remove the unshared object from the table, and restore any previous
0683: * handle.
0684: *
0685: * @param obj
0686: * Non-null object being dumped.
0687: * @param previousHandle
0688: * The handle of the previous identical object dumped
0689: */
0690: private void removeUnsharedReference(Object obj,
0691: Integer previousHandle) {
0692: if (previousHandle != null) {
0693: registerObjectWritten(obj, previousHandle);
0694: } else {
0695: objectsWritten.remove(obj);
0696: }
0697: }
0698:
0699: /**
0700: * Assume object <code>obj</code> has not been dumped yet, and assign a
0701: * handle to it, <code>handle</code>.
0702: *
0703: * @param obj
0704: * Non-null object being dumped.
0705: * @param handle
0706: * An Integer, the handle to this object
0707: *
0708: * @see #nextHandle
0709: */
0710: private void registerObjectWritten(Object obj, Integer handle) {
0711: objectsWritten.put(obj, handle);
0712: }
0713:
0714: /**
0715: * If <code>enableReplaceObject()</code> was activated, computes the
0716: * replacement object for the original object <code>object</code> and
0717: * returns the replacement. Otherwise returns <code>object</code>.
0718: *
0719: * @param object
0720: * Original object for which a replacement may be defined
0721: * @return a possibly new, replacement object for <code>object</code>
0722: *
0723: * @throws IOException
0724: * If any IO problem occurred when trying to resolve the object.
0725: *
0726: * @see #enableReplaceObject
0727: * @see ObjectInputStream#enableResolveObject
0728: * @see ObjectInputStream#resolveObject
0729: */
0730: protected Object replaceObject(Object object) throws IOException {
0731: // By default no object replacement. Subclasses can override
0732: return object;
0733: }
0734:
0735: /**
0736: * Reset the receiver. A marker is written to the stream, so that
0737: * deserialization will also perform a rest at the same point. Objects
0738: * previously written are no longer remembered, so they will be written
0739: * again (instead of a cyclical reference) if found in the object graph.
0740: *
0741: * @throws IOException
0742: * If any IO problem occurred when trying to reset the receiver
0743: */
0744: public void reset() throws IOException {
0745: // First we flush what we have
0746: drain();
0747: /*
0748: * And dump a reset marker, so that the ObjectInputStream can reset
0749: * itself at the same point
0750: */
0751: output.writeByte(TC_RESET);
0752: // Now we reset ourselves
0753: resetState();
0754: }
0755:
0756: /**
0757: * Reset the collection of objects already dumped by the receiver. If the
0758: * objects are found again in the object graph, the receiver will dump them
0759: * again, instead of a handle (cyclic reference).
0760: *
0761: */
0762: private void resetSeenObjects() {
0763: objectsWritten = new IdentityHashMap<Object, Integer>();
0764: currentHandle = baseWireHandle;
0765: }
0766:
0767: /**
0768: * Reset the receiver. The collection of objects already dumped by the
0769: * receiver is reset, and internal structures are also reset so that the
0770: * receiver knows it is in a fresh clean state.
0771: *
0772: */
0773: private void resetState() {
0774: resetSeenObjects();
0775: nestedLevels = 0;
0776: }
0777:
0778: /**
0779: * Set the receiver to use the given protocol version.
0780: *
0781: * @param version
0782: * protocol version to be used
0783: *
0784: * @throws IOException
0785: * If an IO error occurs
0786: */
0787: public void useProtocolVersion(int version) throws IOException {
0788: if (!objectsWritten.isEmpty()) {
0789: // KA028=Cannot set protocol version when stream in use
0790: throw new IllegalStateException(Msg.getString("KA028")); //$NON-NLS-1$
0791: }
0792: if (version != ObjectStreamConstants.PROTOCOL_VERSION_1
0793: && version != ObjectStreamConstants.PROTOCOL_VERSION_2) {
0794: // K00b3=Unknown protocol\: {0}
0795: throw new IllegalArgumentException(Msg.getString(
0796: "K00b3", version)); //$NON-NLS-1$
0797: }
0798: protocolVersion = version;
0799: }
0800:
0801: /**
0802: * Writes the entire contents of the byte array <code>buffer</code> to
0803: * this ObjectOutputStream.
0804: *
0805: * @param buffer
0806: * the buffer to be written
0807: *
0808: * @throws IOException
0809: * If an error occurs attempting to write to this
0810: * ObjectOutputStream.
0811: */
0812: @Override
0813: public void write(byte[] buffer) throws IOException {
0814: checkWritePrimitiveTypes();
0815: primitiveTypes.write(buffer);
0816: }
0817:
0818: /**
0819: * Writes <code>length</code> <code>bytes</code> from the byte array
0820: * <code>buffer</code> starting at offset <code>offset</code> to the
0821: * ObjectOutputStream.
0822: *
0823: * @param buffer
0824: * the buffer to be written
0825: * @param offset
0826: * offset in buffer to get bytes
0827: * @param length
0828: * number of bytes in buffer to write
0829: *
0830: * @throws IOException
0831: * If an error occurs attempting to write to this OutputStream.
0832: */
0833: @Override
0834: public void write(byte[] buffer, int offset, int length)
0835: throws IOException {
0836: checkWritePrimitiveTypes();
0837: primitiveTypes.write(buffer, offset, length);
0838: }
0839:
0840: /**
0841: * Write one byte (<code>value</code>) into the receiver's underlying
0842: * stream.
0843: *
0844: * @param value
0845: * The primitive data to write. Only the lower byte is written.
0846: *
0847: * @throws IOException
0848: * If an IO exception happened when writing the byte.
0849: */
0850: @Override
0851: public void write(int value) throws IOException {
0852: checkWritePrimitiveTypes();
0853: primitiveTypes.write(value);
0854: }
0855:
0856: /**
0857: * Write primitive data of type boolean (<code>value</code>)into the
0858: * receiver's underlying stream.
0859: *
0860: * @param value
0861: * The primitive data to write
0862: *
0863: * @throws IOException
0864: * If an IO exception happened when writing the primitive data.
0865: */
0866: public void writeBoolean(boolean value) throws IOException {
0867: checkWritePrimitiveTypes();
0868: primitiveTypes.writeBoolean(value);
0869: }
0870:
0871: /**
0872: * Write primitive data of type byte (<code>value</code>)into the
0873: * receiver's underlying stream.
0874: *
0875: * @param value
0876: * The primitive data to write
0877: *
0878: * @throws IOException
0879: * If an IO exception happened when writing the primitive data.
0880: */
0881: public void writeByte(int value) throws IOException {
0882: checkWritePrimitiveTypes();
0883: primitiveTypes.writeByte(value);
0884: }
0885:
0886: /**
0887: * Write a String as a sequence of bytes (only lower-order 8 bits of each
0888: * char are written), as primitive data (<code>value</code>) into the
0889: * receiver's underlying stream.
0890: *
0891: * @param value
0892: * The primitive data to write
0893: *
0894: * @throws IOException
0895: * If an IO exception happened when writing the primitive data.
0896: */
0897: public void writeBytes(String value) throws IOException {
0898: checkWritePrimitiveTypes();
0899: primitiveTypes.writeBytes(value);
0900: }
0901:
0902: /**
0903: * Write primitive data of type char (<code>value</code>)into the
0904: * receiver's underlying stream.
0905: *
0906: * @param value
0907: * The primitive data to write
0908: *
0909: * @throws IOException
0910: * If an IO exception happened when writing the primitive data.
0911: */
0912: public void writeChar(int value) throws IOException {
0913: checkWritePrimitiveTypes();
0914: primitiveTypes.writeChar(value);
0915: }
0916:
0917: /**
0918: * Write a String as a sequence of char, as primitive data (<code>value</code>)
0919: * into the receiver's underlying stream.
0920: *
0921: * @param value
0922: * The primitive data to write
0923: *
0924: * @throws IOException
0925: * If an IO exception happened when writing the primitive data.
0926: */
0927: public void writeChars(String value) throws IOException {
0928: checkWritePrimitiveTypes();
0929: primitiveTypes.writeChars(value);
0930: }
0931:
0932: /**
0933: * Write a class descriptor <code>classDesc</code> (an
0934: * <code>ObjectStreamClass</code>) to the stream.
0935: *
0936: * @param classDesc
0937: * The class descriptor (an <code>ObjectStreamClass</code>) to
0938: * be dumped
0939: * @param unshared
0940: * Write the object unshared
0941: * @return the handle assigned to the class descriptor
0942: *
0943: * @throws IOException
0944: * If an IO exception happened when writing the class
0945: * descriptor.
0946: */
0947: private Integer writeClassDesc(ObjectStreamClass classDesc,
0948: boolean unshared) throws IOException {
0949: if (classDesc == null) {
0950: writeNull();
0951: return null;
0952: }
0953: Integer handle = null;
0954: if (!unshared) {
0955: handle = dumpCycle(classDesc);
0956: }
0957: if (handle == null) {
0958: Class<?> classToWrite = classDesc.forClass();
0959: Integer previousHandle = objectsWritten.get(classDesc);
0960: // If we got here, it is a new (non-null) classDesc that will have
0961: // to be registered as well
0962: handle = registerObjectWritten(classDesc);
0963:
0964: if (Proxy.isProxyClass(classToWrite)) {
0965: output.writeByte(TC_PROXYCLASSDESC);
0966: Class<?>[] interfaces = classToWrite.getInterfaces();
0967: output.writeInt(interfaces.length);
0968: for (int i = 0; i < interfaces.length; i++) {
0969: output.writeUTF(interfaces[i].getName());
0970: }
0971: annotateProxyClass(classToWrite);
0972: output.writeByte(TC_ENDBLOCKDATA);
0973: writeClassDescForClass(Proxy.class);
0974: if (unshared) {
0975: // remove reference to unshared object
0976: removeUnsharedReference(classDesc, previousHandle);
0977: }
0978: return handle;
0979: }
0980:
0981: output.writeByte(TC_CLASSDESC);
0982: if (protocolVersion == PROTOCOL_VERSION_1) {
0983: writeNewClassDesc(classDesc);
0984: } else {
0985: // So write...() methods can be used by
0986: // subclasses during writeClassDescriptor()
0987: primitiveTypes = output;
0988: writeClassDescriptor(classDesc);
0989: primitiveTypes = null;
0990: }
0991: // Extra class info (optional)
0992: annotateClass(classToWrite);
0993: drain(); // flush primitive types in the annotation
0994: output.writeByte(TC_ENDBLOCKDATA);
0995: writeClassDesc(classDesc.getSuperclass(), unshared);
0996: if (unshared) {
0997: // remove reference to unshared object
0998: removeUnsharedReference(classDesc, previousHandle);
0999: }
1000: }
1001: return handle;
1002: }
1003:
1004: /**
1005: * Writes a class descriptor (an <code>ObjectStreamClass</code>) that
1006: * corresponds to the <code>java.lang.Class objClass</code> to the stream.
1007: *
1008: * @param objClass
1009: * The class for which a class descriptor (an
1010: * <code>ObjectStreamClass</code>) will be dumped.
1011: * @return the handle assigned to the class descriptor
1012: *
1013: * @throws IOException
1014: * If an IO exception happened when writing the class
1015: * descriptor.
1016: *
1017: */
1018: private Integer writeClassDescForClass(Class<?> objClass)
1019: throws IOException {
1020: return writeClassDesc(ObjectStreamClass.lookup(objClass), false);
1021: }
1022:
1023: /**
1024: * Writes a handle representing a cyclic reference (object previously
1025: * dumped).
1026: *
1027: * @param handle
1028: * The Integer handle that represents an object previously seen
1029: *
1030: * @throws IOException
1031: * If an IO exception happened when writing the cyclic
1032: * reference.
1033: */
1034: private void writeCyclicReference(Integer handle)
1035: throws IOException {
1036: output.writeByte(TC_REFERENCE);
1037: output.writeInt(handle.intValue());
1038: }
1039:
1040: /**
1041: * Write primitive data of type double (<code>value</code>)into the
1042: * receiver's underlying stream.
1043: *
1044: * @param value
1045: * The primitive data to write
1046: *
1047: * @throws IOException
1048: * If an IO exception happened when writing the primitive data.
1049: */
1050: public void writeDouble(double value) throws IOException {
1051: checkWritePrimitiveTypes();
1052: primitiveTypes.writeDouble(value);
1053: }
1054:
1055: /**
1056: * Writes a collection of field descriptors (name, type name, etc) for the
1057: * class descriptor <code>classDesc</code> (an
1058: * <code>ObjectStreamClass</code>)
1059: *
1060: * @param classDesc
1061: * The class descriptor (an <code>ObjectStreamClass</code>)
1062: * for which to write field information
1063: * @param externalizable
1064: * true if the descriptors are externalizable
1065: *
1066: * @throws IOException
1067: * If an IO exception happened when writing the field
1068: * descriptors.
1069: *
1070: * @see #writeObject(Object)
1071: */
1072: private void writeFieldDescriptors(ObjectStreamClass classDesc,
1073: boolean externalizable) throws IOException {
1074: Class<?> loadedClass = classDesc.forClass();
1075: ObjectStreamField[] fields = null;
1076: int fieldCount = 0;
1077:
1078: // The fields of String are not needed since Strings are treated as
1079: // primitive types
1080: if (!externalizable
1081: && loadedClass != ObjectStreamClass.STRINGCLASS) {
1082: fields = classDesc.fields();
1083: fieldCount = fields.length;
1084: }
1085:
1086: // Field count
1087: output.writeShort(fieldCount);
1088: // Field names
1089: for (int i = 0; i < fieldCount; i++) {
1090: ObjectStreamField f = fields[i];
1091: output.writeByte(f.getTypeCode());
1092: output.writeUTF(f.getName());
1093: if (!f.isPrimitive()) {
1094: writeObject(f.getTypeString());
1095: }
1096: }
1097: }
1098:
1099: /**
1100: * Write the fields of the object being dumped. The stream will use the
1101: * currently active <code>PutField</code> object, allowing users to dump
1102: * emulated fields, for cross-loading compatibility when a class definition
1103: * changes.
1104: *
1105: * @throws IOException
1106: * If an IO error occurs
1107: *
1108: * @see #putFields
1109: */
1110: public void writeFields() throws IOException {
1111: // Has to have fields to write
1112: if (currentPutField == null) {
1113: throw new NotActiveException();
1114: }
1115: writeFieldValues(currentPutField);
1116: }
1117:
1118: /**
1119: * Writes a collection of field values for the emulated fields
1120: * <code>emulatedFields</code>
1121: *
1122: * @param emulatedFields
1123: * an <code>EmulatedFieldsForDumping</code>, concrete subclass
1124: * of <code>PutField</code>
1125: *
1126: * @throws IOException
1127: * If an IO exception happened when writing the field values.
1128: *
1129: * @see #writeFields
1130: * @see #writeObject(Object)
1131: */
1132: private void writeFieldValues(
1133: EmulatedFieldsForDumping emulatedFields) throws IOException {
1134: EmulatedFields accessibleSimulatedFields = emulatedFields
1135: .emulatedFields(); // Access internal fields which we can
1136: // set/get. Users can't do this.
1137: EmulatedFields.ObjectSlot[] slots = accessibleSimulatedFields
1138: .slots();
1139: for (int i = 0; i < slots.length; i++) {
1140: EmulatedFields.ObjectSlot slot = slots[i];
1141: Object fieldValue = slot.getFieldValue();
1142: Class<?> type = slot.getField().getType();
1143: // WARNING - default values exist for each primitive type
1144: if (type == Integer.TYPE) {
1145: output
1146: .writeInt(fieldValue != null ? ((Integer) fieldValue)
1147: .intValue()
1148: : 0);
1149: } else if (type == Byte.TYPE) {
1150: output
1151: .writeByte(fieldValue != null ? ((Byte) fieldValue)
1152: .byteValue()
1153: : (byte) 0);
1154: } else if (type == Character.TYPE) {
1155: output
1156: .writeChar(fieldValue != null ? ((Character) fieldValue)
1157: .charValue()
1158: : (char) 0);
1159: } else if (type == Short.TYPE) {
1160: output
1161: .writeShort(fieldValue != null ? ((Short) fieldValue)
1162: .shortValue()
1163: : (short) 0);
1164: } else if (type == Boolean.TYPE) {
1165: output
1166: .writeBoolean(fieldValue != null ? ((Boolean) fieldValue)
1167: .booleanValue()
1168: : false);
1169: } else if (type == Long.TYPE) {
1170: output
1171: .writeLong(fieldValue != null ? ((Long) fieldValue)
1172: .longValue()
1173: : (long) 0);
1174: } else if (type == Float.TYPE) {
1175: output
1176: .writeFloat(fieldValue != null ? ((Float) fieldValue)
1177: .floatValue()
1178: : (float) 0);
1179: } else if (type == Double.TYPE) {
1180: output
1181: .writeDouble(fieldValue != null ? ((Double) fieldValue)
1182: .doubleValue()
1183: : (double) 0);
1184: } else {
1185: // Either array or Object
1186: writeObject(fieldValue);
1187: }
1188: }
1189: }
1190:
1191: /**
1192: * Writes a collection of field values for the fields described by class
1193: * descriptor <code>classDesc</code> (an <code>ObjectStreamClass</code>).
1194: * This is the default mechanism, when emulated fields (an
1195: * <code>PutField</code>) are not used. Actual values to dump are fetched
1196: * directly from object <code>obj</code>.
1197: *
1198: * @param obj
1199: * Instance from which to fetch field values to dump.
1200: * @param classDesc
1201: * A class descriptor (an <code>ObjectStreamClass</code>)
1202: * defining which fields should be dumped.
1203: *
1204: * @throws IOException
1205: * If an IO exception happened when writing the field values.
1206: *
1207: * @see #writeObject(Object)
1208: */
1209: private void writeFieldValues(Object obj,
1210: ObjectStreamClass classDesc) throws IOException {
1211: ObjectStreamField[] fields = classDesc.fields();
1212: Class<?> declaringClass = classDesc.forClass();
1213: for (int i = 0; i < fields.length; i++) {
1214: try {
1215: // Code duplication starts, just because Java is typed
1216: ObjectStreamField fieldDesc = fields[i];
1217: if (fieldDesc.isPrimitive()) {
1218: switch (fieldDesc.getTypeCode()) {
1219: case 'B':
1220: output.writeByte(getFieldByte(obj,
1221: declaringClass, fieldDesc.getName()));
1222: break;
1223: case 'C':
1224: output.writeChar(getFieldChar(obj,
1225: declaringClass, fieldDesc.getName()));
1226: break;
1227: case 'D':
1228: output.writeDouble(getFieldDouble(obj,
1229: declaringClass, fieldDesc.getName()));
1230: break;
1231: case 'F':
1232: output.writeFloat(getFieldFloat(obj,
1233: declaringClass, fieldDesc.getName()));
1234: break;
1235: case 'I':
1236: output.writeInt(getFieldInt(obj,
1237: declaringClass, fieldDesc.getName()));
1238: break;
1239: case 'J':
1240: output.writeLong(getFieldLong(obj,
1241: declaringClass, fieldDesc.getName()));
1242: break;
1243: case 'S':
1244: output.writeShort(getFieldShort(obj,
1245: declaringClass, fieldDesc.getName()));
1246: break;
1247: case 'Z':
1248: output.writeBoolean(getFieldBool(obj,
1249: declaringClass, fieldDesc.getName()));
1250: break;
1251: default:
1252: throw new IOException(
1253: org.apache.harmony.luni.util.Msg
1254: .getString(
1255: "K00d5", fieldDesc.getTypeCode())); //$NON-NLS-1$
1256: }
1257: } else {
1258: // Object type (array included).
1259: Object field = getFieldObj(obj, declaringClass,
1260: fieldDesc.getName(), fieldDesc
1261: .getTypeString());
1262: if (fieldDesc.isUnshared()) {
1263: writeUnshared(field);
1264: } else {
1265: writeObject(field);
1266: }
1267: }
1268: } catch (NoSuchFieldError nsf) {
1269: // The user defined serialPersistentFields but did not provide
1270: // the glue to transfer values,
1271: // (in writeObject) so we end up using the default mechanism and
1272: // fail to set the emulated field
1273: throw new InvalidClassException(classDesc.getName());
1274: }
1275: }
1276: }
1277:
1278: /**
1279: * Write primitive data of type float (<code>value</code>)into the
1280: * receiver's underlying stream.
1281: *
1282: * @param value
1283: * The primitive data to write
1284: *
1285: * @throws IOException
1286: * If an IO exception happened when writing the primitive data.
1287: */
1288: public void writeFloat(float value) throws IOException {
1289: checkWritePrimitiveTypes();
1290: primitiveTypes.writeFloat(value);
1291: }
1292:
1293: /**
1294: * Walks the hierarchy of classes described by class descriptor
1295: * <code>classDesc</code> and writes the field values corresponding to
1296: * fields declared by the corresponding class descriptor. The instance to
1297: * fetch field values from is <code>object</code>. If the class
1298: * (corresponding to class descriptor <code>classDesc</code>) defines
1299: * private instance method <code>writeObject</code> it will be used to
1300: * dump field values.
1301: *
1302: * @param object
1303: * Instance from which to fetch field values to dump.
1304: * @param classDesc
1305: * A class descriptor (an <code>ObjectStreamClass</code>)
1306: * defining which fields should be dumped.
1307: *
1308: * @throws IOException
1309: * If an IO exception happened when writing the field values in
1310: * the hierarchy.
1311: * @throws NotActiveException
1312: * If the given object is not active
1313: *
1314: * @see #defaultWriteObject
1315: * @see #writeObject(Object)
1316: */
1317: private void writeHierarchy(Object object,
1318: ObjectStreamClass classDesc) throws IOException,
1319: NotActiveException {
1320: // We can't be called from just anywhere. There are rules.
1321: if (object == null) {
1322: throw new NotActiveException();
1323: }
1324:
1325: // Fields are written from class closest to Object to leaf class
1326: // (down the chain)
1327: if (classDesc.getSuperclass() != null) {
1328: // first
1329: writeHierarchy(object, classDesc.getSuperclass());
1330: }
1331:
1332: // Have to do this before calling defaultWriteObject or anything
1333: // that calls defaultWriteObject
1334: currentObject = object;
1335: currentClass = classDesc;
1336:
1337: // See if the object has a writeObject method. If so, run it
1338: boolean executed = false;
1339: try {
1340: if (classDesc.hasMethodWriteObject()) {
1341: final Method method = classDesc.getMethodWriteObject();
1342: try {
1343: method.invoke(object, new Object[] { this });
1344: executed = true;
1345: } catch (InvocationTargetException e) {
1346: Throwable ex = e.getTargetException();
1347: if (ex instanceof RuntimeException) {
1348: throw (RuntimeException) ex;
1349: } else if (ex instanceof Error) {
1350: throw (Error) ex;
1351: }
1352: throw (IOException) ex;
1353: } catch (IllegalAccessException e) {
1354: throw new RuntimeException(e.toString());
1355: }
1356: }
1357:
1358: if (executed) {
1359: drain();
1360: output.writeByte(TC_ENDBLOCKDATA);
1361: } else {
1362: // If the object did not have a writeMethod, call
1363: // defaultWriteObject
1364: defaultWriteObject();
1365: }
1366: } finally {
1367: // Cleanup, needs to run always so that we can later detect
1368: // invalid calls to defaultWriteObject
1369: currentObject = null;
1370: currentClass = null;
1371: currentPutField = null;
1372: }
1373: }
1374:
1375: /**
1376: * Write primitive data of type int (<code>value</code>)into the
1377: * receiver's underlying stream.
1378: *
1379: * @param value
1380: * The primitive data to write
1381: *
1382: * @throws IOException
1383: * If an IO exception happened when writing the primitive data.
1384: */
1385: public void writeInt(int value) throws IOException {
1386: checkWritePrimitiveTypes();
1387: primitiveTypes.writeInt(value);
1388: }
1389:
1390: /**
1391: * Write primitive data of type long (<code>value</code>)into the
1392: * receiver's underlying stream.
1393: *
1394: * @param value
1395: * The primitive data to write
1396: *
1397: * @throws IOException
1398: * If an IO exception happened when writing the primitive data.
1399: */
1400: public void writeLong(long value) throws IOException {
1401: checkWritePrimitiveTypes();
1402: primitiveTypes.writeLong(value);
1403: }
1404:
1405: /**
1406: * Write array <code>array</code> of class <code>arrayClass</code> with
1407: * component type <code>componentType</code> into the receiver. It is
1408: * assumed the array has not been dumped yet. Return an <code>Integer</code>
1409: * that represents the handle for this object (array) which is dumped here.
1410: *
1411: * @param array
1412: * The array object to dump
1413: * @param arrayClass
1414: * A <code>java.lang.Class</code> representing the class of the
1415: * array
1416: * @param componentType
1417: * A <code>java.lang.Class</code> representing the array
1418: * component type
1419: * @return the handle assigned to the array
1420: *
1421: * @throws IOException
1422: * If an IO exception happened when writing the array.
1423: */
1424: private Integer writeNewArray(Object array, Class<?> arrayClass,
1425: Class<?> componentType, boolean unshared)
1426: throws IOException {
1427: output.writeByte(TC_ARRAY);
1428: writeClassDescForClass(arrayClass);
1429:
1430: Integer previousHandle = objectsWritten.get(array);
1431: Integer handle = registerObjectWritten(array);
1432: if (unshared) {
1433: // remove reference to unshared object
1434: removeUnsharedReference(array, previousHandle);
1435: }
1436:
1437: // Now we have code duplication just because Java is typed. We have to
1438: // write N elements and assign to array positions, but we must typecast
1439: // the array first, and also call different methods depending on the
1440: // elements.
1441:
1442: if (componentType.isPrimitive()) {
1443: if (componentType == Integer.TYPE) {
1444: int[] intArray = (int[]) array;
1445: output.writeInt(intArray.length);
1446: for (int i = 0; i < intArray.length; i++) {
1447: output.writeInt(intArray[i]);
1448: }
1449: } else if (componentType == Byte.TYPE) {
1450: byte[] byteArray = (byte[]) array;
1451: output.writeInt(byteArray.length);
1452: output.write(byteArray, 0, byteArray.length);
1453: } else if (componentType == Character.TYPE) {
1454: char[] charArray = (char[]) array;
1455: output.writeInt(charArray.length);
1456: for (int i = 0; i < charArray.length; i++) {
1457: output.writeChar(charArray[i]);
1458: }
1459: } else if (componentType == Short.TYPE) {
1460: short[] shortArray = (short[]) array;
1461: output.writeInt(shortArray.length);
1462: for (int i = 0; i < shortArray.length; i++) {
1463: output.writeShort(shortArray[i]);
1464: }
1465: } else if (componentType == Boolean.TYPE) {
1466: boolean[] booleanArray = (boolean[]) array;
1467: output.writeInt(booleanArray.length);
1468: for (int i = 0; i < booleanArray.length; i++) {
1469: output.writeBoolean(booleanArray[i]);
1470: }
1471: } else if (componentType == Long.TYPE) {
1472: long[] longArray = (long[]) array;
1473: output.writeInt(longArray.length);
1474: for (int i = 0; i < longArray.length; i++) {
1475: output.writeLong(longArray[i]);
1476: }
1477: } else if (componentType == Float.TYPE) {
1478: float[] floatArray = (float[]) array;
1479: output.writeInt(floatArray.length);
1480: for (int i = 0; i < floatArray.length; i++) {
1481: output.writeFloat(floatArray[i]);
1482: }
1483: } else if (componentType == Double.TYPE) {
1484: double[] doubleArray = (double[]) array;
1485: output.writeInt(doubleArray.length);
1486: for (int i = 0; i < doubleArray.length; i++) {
1487: output.writeDouble(doubleArray[i]);
1488: }
1489: } else {
1490: throw new InvalidClassException(
1491: org.apache.harmony.luni.util.Msg.getString(
1492: "K00d7", arrayClass.getName())); //$NON-NLS-1$
1493: }
1494: } else {
1495: // Array of Objects
1496: Object[] objectArray = (Object[]) array;
1497: output.writeInt(objectArray.length);
1498: for (int i = 0; i < objectArray.length; i++) {
1499: writeObject(objectArray[i]);
1500: }
1501: }
1502: return handle;
1503: }
1504:
1505: /**
1506: * Write class <code>object</code> into the receiver. It is assumed the
1507: * class has not been dumped yet. Classes are not really dumped, but a class
1508: * descriptor (<code>ObjectStreamClass</code>) that corresponds to them.
1509: * Return an <code>Integer</code> that represents the handle for this
1510: * object (class) which is dumped here.
1511: *
1512: * @param object
1513: * The <code>java.lang.Class</code> object to dump
1514: * @return the handle assigned to the class being dumped
1515: *
1516: * @throws IOException
1517: * If an IO exception happened when writing the class.
1518: */
1519: private Integer writeNewClass(Class<?> object, boolean unshared)
1520: throws IOException {
1521: output.writeByte(TC_CLASS);
1522:
1523: // Instances of java.lang.Class are always Serializable, even if their
1524: // instances aren't (e.g. java.lang.Object.class).
1525: // We cannot call lookup because it returns null if the parameter
1526: // represents instances that cannot be serialized, and that is not what
1527: // we want.
1528:
1529: // The handle for the classDesc is NOT the handle for the class object
1530: // being dumped. We must allocate a new handle and return it.
1531: if (object.isEnum()) {
1532: writeEnumDesc(object, unshared);
1533: } else {
1534: writeClassDesc(ObjectStreamClass.lookupStreamClass(object),
1535: unshared);
1536: }
1537:
1538: Integer previousHandle = objectsWritten.get(object);
1539: Integer handle = registerObjectWritten(object);
1540: if (unshared) {
1541: // remove reference to unshared object
1542: removeUnsharedReference(object, previousHandle);
1543: }
1544:
1545: return handle;
1546: }
1547:
1548: /**
1549: * Write class descriptor <code>classDesc</code> into the receiver. It is
1550: * assumed the class descriptor has not been dumped yet. The class
1551: * descriptors for the superclass chain will be dumped as well. Return an
1552: * <code>Integer</code> that represents the handle for this object (class
1553: * descriptor) which is dumped here.
1554: *
1555: * @param classDesc
1556: * The <code>ObjectStreamClass</code> object to dump
1557: *
1558: * @throws IOException
1559: * If an IO exception happened when writing the class
1560: * descriptor.
1561: */
1562: private void writeNewClassDesc(ObjectStreamClass classDesc)
1563: throws IOException {
1564: output.writeUTF(classDesc.getName());
1565: output.writeLong(classDesc.getSerialVersionUID());
1566: byte flags = classDesc.getFlags();
1567: boolean externalizable = false;
1568: externalizable = ObjectStreamClass.isExternalizable(classDesc
1569: .forClass());
1570:
1571: if (externalizable) {
1572: if (protocolVersion == PROTOCOL_VERSION_1) {
1573: flags &= NOT_SC_BLOCK_DATA;
1574: } else {
1575: // Change for 1.2. Objects can be saved in old format
1576: // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2).
1577: flags |= SC_BLOCK_DATA;
1578: }
1579: }
1580: output.writeByte(flags);
1581: if ((SC_ENUM | SC_SERIALIZABLE) != classDesc.getFlags()) {
1582: writeFieldDescriptors(classDesc, externalizable);
1583: } else {
1584: // enum write no fields
1585: output.writeShort(0);
1586: }
1587: }
1588:
1589: /**
1590: * Write class descriptor <code>classDesc</code> into the receiver.
1591: *
1592: * @param classDesc
1593: * The <code>ObjectStreamClass</code> object to dump
1594: *
1595: * @throws IOException
1596: * If an IO exception happened when writing the class
1597: * descriptor.
1598: */
1599: protected void writeClassDescriptor(ObjectStreamClass classDesc)
1600: throws IOException {
1601: writeNewClassDesc(classDesc);
1602: }
1603:
1604: /**
1605: * Write exception <code>ex</code> into the receiver. It is assumed the
1606: * exception has not been dumped yet. Return an <code>Integer</code> that
1607: * represents the handle for this object (exception) which is dumped here.
1608: * This is used to dump the exception instance that happened (if any) when
1609: * dumping the original object graph. The set of seen objects will be reset
1610: * just before and just after dumping this exception object.
1611: *
1612: * When exceptions are found normally in the object graph, they are dumped
1613: * as a regular object, and not by this method. In that case, the set of
1614: * "known objects" is not reset.
1615: *
1616: * @param ex
1617: * Exception object to dump
1618: *
1619: * @throws IOException
1620: * If an IO exception happened when writing the exception
1621: * object.
1622: */
1623: private void writeNewException(Exception ex) throws IOException {
1624: output.writeByte(TC_EXCEPTION);
1625: resetSeenObjects();
1626: writeObjectInternal(ex, false, false, false); // No replacements
1627: resetSeenObjects();
1628: }
1629:
1630: /**
1631: * Write object <code>object</code> of class <code>theClass</code> into
1632: * the receiver. It is assumed the object has not been dumped yet. Return an
1633: * <code>Integer</code> that represents the handle for this object which
1634: * is dumped here.
1635: *
1636: * If the object implements <code>Externalizable</code> its
1637: * <code>writeExternal</code> is called. Otherwise, all fields described
1638: * by the class hierarchy is dumped. Each class can define how its declared
1639: * instance fields are dumped by defining a private method
1640: * <code>writeObject</code>
1641: *
1642: * @param object
1643: * The object to dump
1644: * @param theClass
1645: * A <code>java.lang.Class</code> representing the class of the
1646: * object
1647: * @param unshared
1648: * Write the object unshared
1649: * @return the handle assigned to the object
1650: *
1651: * @throws IOException
1652: * If an IO exception happened when writing the object.
1653: */
1654: private Integer writeNewObject(Object object, Class<?> theClass,
1655: boolean unshared) throws IOException {
1656: // Not String, not null, not array, not cyclic reference
1657:
1658: EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
1659: currentPutField = null; // null it, to make sure one will be computed if
1660: // needed
1661:
1662: boolean externalizable = ObjectStreamClass
1663: .isExternalizable(theClass);
1664: boolean serializable = ObjectStreamClass
1665: .isSerializable(theClass);
1666: if (!externalizable && !serializable) {
1667: // Object is neither externalizable nor serializable. Error
1668: throw new NotSerializableException(theClass.getName());
1669: }
1670:
1671: // Either serializable or externalizable, now we can save info
1672: output.writeByte(TC_OBJECT);
1673: writeClassDescForClass(theClass);
1674: Integer previousHandle = objectsWritten.get(object);
1675: Integer handle = registerObjectWritten(object);
1676:
1677: // This is how we know what to do in defaultWriteObject. And it is also
1678: // used by defaultWriteObject to check if it was called from an invalid
1679: // place.
1680: // It allows writeExternal to call defaultWriteObject and have it work.
1681: currentObject = object;
1682: currentClass = ObjectStreamClass.lookup(theClass);
1683: try {
1684: if (externalizable) {
1685: boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1;
1686: if (noBlockData) {
1687: primitiveTypes = output;
1688: }
1689: // Object is externalizable, just call its own method
1690: ((Externalizable) object).writeExternal(this );
1691: if (noBlockData) {
1692: primitiveTypes = null;
1693: } else {
1694: // Similar to the code in writeHierarchy when object
1695: // implements writeObject.
1696: // Any primitive data has to be flushed and a tag must be
1697: // written
1698: drain();
1699: output.writeByte(TC_ENDBLOCKDATA);
1700: }
1701: } else { // If it got here, it has to be Serializable
1702: // Object is serializable. Walk the class chain writing the
1703: // fields
1704: writeHierarchy(object, currentClass);
1705: }
1706: } finally {
1707: // Cleanup, needs to run always so that we can later detect invalid
1708: // calls to defaultWriteObject
1709: if (unshared) {
1710: // remove reference to unshared object
1711: removeUnsharedReference(object, previousHandle);
1712: }
1713: currentObject = null;
1714: currentClass = null;
1715: currentPutField = originalCurrentPutField;
1716: }
1717:
1718: return handle;
1719: }
1720:
1721: /**
1722: * Write String <code>object</code> into the receiver. It is assumed the
1723: * String has not been dumped yet. Return an <code>Integer</code> that
1724: * represents the handle for this object (String) which is dumped here.
1725: * Strings are saved in UTF format.
1726: *
1727: * @param object
1728: * The <code>java.lang.String</code> object to dump
1729: * @return the handle assigned to the String being dumped
1730: *
1731: * @throws IOException
1732: * If an IO exception happened when writing the String.
1733: */
1734: private Integer writeNewString(String object, boolean unshared)
1735: throws IOException {
1736: long count = output.countUTFBytes(object);
1737: if (count <= 0xffff) {
1738: output.writeByte(TC_STRING);
1739: output.writeShort((short) count);
1740: } else {
1741: output.writeByte(TC_LONGSTRING);
1742: output.writeLong(count);
1743: }
1744: output.writeUTFBytes(object, count);
1745:
1746: Integer previousHandle = objectsWritten.get(object);
1747: Integer handle = registerObjectWritten(object);
1748: if (unshared) {
1749: // remove reference to unshared object
1750: removeUnsharedReference(object, previousHandle);
1751: }
1752: return handle;
1753: }
1754:
1755: /**
1756: * Write a special tag that indicates the value <code>null</code> into the
1757: * receiver.
1758: *
1759: * @throws IOException
1760: * If an IO exception happened when writing the tag for
1761: * <code>null</code>.
1762: */
1763: private void writeNull() throws IOException {
1764: output.writeByte(TC_NULL);
1765: }
1766:
1767: /**
1768: * Write object <code>object</code> into the receiver's underlying stream.
1769: *
1770: * @param object
1771: * The object to write
1772: *
1773: * @throws IOException
1774: * If an IO exception happened when writing the object
1775: *
1776: * @see ObjectInputStream#readObject()
1777: */
1778: public final void writeObject(Object object) throws IOException {
1779: writeObject(object, false);
1780: }
1781:
1782: /**
1783: * Write object <code>object</code> into the receiver's underlying stream
1784: * unshared with previously written identical objects.
1785: *
1786: * @param object
1787: * The object to write
1788: *
1789: * @throws IOException
1790: * If an IO exception happened when writing the object
1791: *
1792: * @see ObjectInputStream#readObject()
1793: */
1794: public void writeUnshared(Object object) throws IOException {
1795: writeObject(object, true);
1796: }
1797:
1798: private void writeObject(Object object, boolean unshared)
1799: throws IOException {
1800: boolean setOutput = (primitiveTypes == output);
1801: if (setOutput) {
1802: primitiveTypes = null;
1803: }
1804: // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow
1805: // behavior overriding.
1806: if (subclassOverridingImplementation && !unshared) {
1807: writeObjectOverride(object);
1808: } else {
1809:
1810: try {
1811: // First we need to flush primitive types if they were written
1812: drain();
1813: // Actual work, and class-based replacement should be computed
1814: // if needed.
1815: writeObjectInternal(object, unshared, true, true);
1816: if (setOutput) {
1817: primitiveTypes = output;
1818: }
1819: } catch (IOException ioEx1) {
1820: // This will make it pass through until the top caller. It also
1821: // lets it pass through the nested exception.
1822: if (nestedLevels == 0 && ioEx1 != nestedException) {
1823: try {
1824: writeNewException(ioEx1);
1825: } catch (IOException ioEx2) {
1826: nestedException.fillInStackTrace();
1827: throw nestedException;
1828: }
1829: }
1830: throw ioEx1; // and then we propagate the original exception
1831: }
1832: }
1833: }
1834:
1835: /**
1836: * Write object <code>object</code> into the receiver's underlying stream.
1837: *
1838: * @param object
1839: * The object to write
1840: * @param unshared
1841: * Write the object unshared
1842: * @param computeClassBasedReplacement
1843: * A boolean indicating if class-based replacement should be
1844: * computed (if supported) for the object.
1845: * @param computeStreamReplacement
1846: * A boolean indicating if stream-based replacement should be
1847: * computed (if supported) for the object.
1848: * @return the handle assigned to the final object being dumped
1849: *
1850: * @throws IOException
1851: * If an IO exception happened when writing the object
1852: *
1853: * @see ObjectInputStream#readObject()
1854: */
1855: private Integer writeObjectInternal(Object object,
1856: boolean unshared, boolean computeClassBasedReplacement,
1857: boolean computeStreamReplacement) throws IOException {
1858:
1859: if (object == null) {
1860: writeNull();
1861: return null;
1862: }
1863: Integer handle = null;
1864: if (!unshared) {
1865: handle = dumpCycle(object);
1866: if (handle != null) {
1867: return handle; // cyclic reference
1868: }
1869: }
1870:
1871: // Non-null object, first time seen...
1872: Class<?> objClass = object.getClass();
1873: nestedLevels++;
1874: try {
1875:
1876: if (!(enableReplace && computeStreamReplacement)) {
1877: // Is it a Class ?
1878: if (objClass == ObjectStreamClass.CLASSCLASS) {
1879: return writeNewClass((Class<?>) object, unshared);
1880: }
1881: // Is it an ObjectStreamClass ?
1882: if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1883: return writeClassDesc((ObjectStreamClass) object,
1884: unshared);
1885: }
1886: }
1887:
1888: if (ObjectStreamClass.isSerializable(object.getClass())
1889: && computeClassBasedReplacement) {
1890: ObjectStreamClass clDesc = ObjectStreamClass
1891: .lookupStreamClass(objClass);
1892: if (clDesc.hasMethodWriteReplace()) {
1893: Method methodWriteReplace = clDesc
1894: .getMethodWriteReplace();
1895: Object replObj = null;
1896: try {
1897: replObj = methodWriteReplace.invoke(object,
1898: (Object[]) null);
1899: } catch (IllegalAccessException iae) {
1900: replObj = object;
1901: } catch (InvocationTargetException ite) {
1902: // WARNING - Not sure this is the right thing to do
1903: // if we can't run the method
1904: Throwable target = ite.getTargetException();
1905: if (target instanceof ObjectStreamException) {
1906: throw (ObjectStreamException) target;
1907: } else if (target instanceof Error) {
1908: throw (Error) target;
1909: } else {
1910: throw (RuntimeException) target;
1911: }
1912: }
1913: if (replObj != object) {
1914: // All over, class-based replacement off this time.
1915: Integer replacementHandle = writeObjectInternal(
1916: replObj, false, false,
1917: computeStreamReplacement);
1918: // Make the original object also map to the same
1919: // handle.
1920: if (replacementHandle != null) {
1921: registerObjectWritten(object,
1922: replacementHandle);
1923: }
1924: return replacementHandle;
1925: }
1926: }
1927:
1928: }
1929:
1930: // We get here either if class-based replacement was not needed or
1931: // if it was needed but produced the same object or if it could not
1932: // be computed.
1933: if (enableReplace && computeStreamReplacement) {
1934: // Now we compute the stream-defined replacement.
1935: Object streamReplacement = replaceObject(object);
1936: if (streamReplacement != object) {
1937: // All over, class-based replacement off this time.
1938: Integer replacementHandle = writeObjectInternal(
1939: streamReplacement, false,
1940: computeClassBasedReplacement, false);
1941: // Make the original object also map to the same handle.
1942: if (replacementHandle != null) {
1943: registerObjectWritten(object, replacementHandle);
1944: }
1945: return replacementHandle;
1946: }
1947: }
1948:
1949: // We get here if stream-based replacement produced the same object
1950:
1951: // Is it a Class ?
1952: if (objClass == ObjectStreamClass.CLASSCLASS) {
1953: return writeNewClass((Class<?>) object, unshared);
1954: }
1955:
1956: // Is it an ObjectStreamClass ?
1957: if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1958: return writeClassDesc((ObjectStreamClass) object,
1959: unshared);
1960: }
1961:
1962: // Is it a String ? (instanceof, but == is faster)
1963: if (objClass == ObjectStreamClass.STRINGCLASS) {
1964: return writeNewString((String) object, unshared);
1965: }
1966:
1967: // Is it an Array ?
1968: if (objClass.isArray()) {
1969: return writeNewArray(object, objClass, objClass
1970: .getComponentType(), unshared);
1971: }
1972:
1973: if (object instanceof Enum) {
1974: return writeNewEnum(object, objClass, unshared);
1975: }
1976:
1977: // Not a String or Class or Array. Default procedure.
1978: return writeNewObject(object, objClass, unshared);
1979: } finally {
1980: nestedLevels--;
1981: }
1982: }
1983:
1984: // write for Enum Class Desc only, which is different from other classes
1985: private ObjectStreamClass writeEnumDesc(Class<?> theClass,
1986: boolean unshared) throws IOException {
1987: // write classDesc, classDesc for enum is different
1988: ObjectStreamClass classDesc = ObjectStreamClass
1989: .lookup(theClass);
1990: // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM)
1991: classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
1992: Integer previousHandle = objectsWritten.get(classDesc);
1993: Integer handle = null;
1994: if (!unshared) {
1995: handle = dumpCycle(classDesc);
1996: }
1997: if (handle == null) {
1998: Class<?> classToWrite = classDesc.forClass();
1999: // If we got here, it is a new (non-null) classDesc that will have
2000: // to be registered as well
2001: registerObjectWritten(classDesc);
2002:
2003: output.writeByte(TC_CLASSDESC);
2004: if (protocolVersion == PROTOCOL_VERSION_1) {
2005: writeNewClassDesc(classDesc);
2006: } else {
2007: // So write...() methods can be used by
2008: // subclasses during writeClassDescriptor()
2009: primitiveTypes = output;
2010: writeClassDescriptor(classDesc);
2011: primitiveTypes = null;
2012: }
2013: // Extra class info (optional)
2014: annotateClass(classToWrite);
2015: drain(); // flush primitive types in the annotation
2016: output.writeByte(TC_ENDBLOCKDATA);
2017: // write super class
2018: ObjectStreamClass super Class = classDesc.getSuperclass();
2019: if (null != super Class) {
2020: // super class is also enum
2021: super Class.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
2022: writeEnumDesc(super Class.forClass(), unshared);
2023: } else {
2024: output.writeByte(TC_NULL);
2025: }
2026: if (unshared) {
2027: // remove reference to unshared object
2028: removeUnsharedReference(classDesc, previousHandle);
2029: }
2030: }
2031: return classDesc;
2032: }
2033:
2034: private Integer writeNewEnum(Object object, Class<?> theClass,
2035: boolean unshared) throws IOException {
2036: // write new Enum
2037: EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
2038: // null it, to make sure one will be computed if needed
2039: currentPutField = null;
2040:
2041: output.writeByte(TC_ENUM);
2042: while (theClass != null && !theClass.isEnum()) {
2043: // write enum only
2044: theClass = theClass.getSuperclass();
2045: }
2046: ObjectStreamClass classDesc = writeEnumDesc(theClass, unshared);
2047:
2048: Integer previousHandle = objectsWritten.get(object);
2049: Integer handle = registerObjectWritten(object);
2050:
2051: ObjectStreamField[] fields = classDesc.getSuperclass().fields();
2052: Class<?> declaringClass = classDesc.getSuperclass().forClass();
2053: // Only write field "name" for enum class, which is the second field of
2054: // enum, that is fields[1]. Ignore all non-fields and fields.length < 2
2055: if (null != fields && fields.length > 1) {
2056: String str = (String) getFieldObj(object, declaringClass,
2057: fields[1].getName(), fields[1].getTypeString());
2058: Integer strhandle = null;
2059: if (!unshared) {
2060: strhandle = dumpCycle(str);
2061: }
2062: if (null == strhandle) {
2063: writeNewString(str, unshared);
2064: }
2065: }
2066:
2067: if (unshared) {
2068: // remove reference to unshared object
2069: removeUnsharedReference(object, previousHandle);
2070: }
2071: currentPutField = originalCurrentPutField;
2072: return handle;
2073: }
2074:
2075: /**
2076: * Method to be overridden by subclasses to write <code>object</code> into
2077: * the receiver's underlying stream.
2078: *
2079: * @param object
2080: * the object
2081: *
2082: * @throws IOException
2083: * If an IO exception happened when writing the object
2084: */
2085: protected void writeObjectOverride(Object object)
2086: throws IOException {
2087: if (!subclassOverridingImplementation) {
2088: // Subclasses must override.
2089: throw new IOException();
2090: }
2091: }
2092:
2093: /**
2094: * Write primitive data of type short (<code>value</code>)into the
2095: * receiver's underlying stream.
2096: *
2097: * @param value
2098: * The primitive data to write
2099: *
2100: * @throws IOException
2101: * If an IO exception happened when writing the primitive data.
2102: */
2103: public void writeShort(int value) throws IOException {
2104: checkWritePrimitiveTypes();
2105: primitiveTypes.writeShort(value);
2106: }
2107:
2108: /**
2109: * Writes the ObjectOutputStream header into the underlying stream.
2110: *
2111: * @throws IOException
2112: * If an IO exception happened when writing the stream header.
2113: */
2114: protected void writeStreamHeader() throws IOException {
2115: output.writeShort(STREAM_MAGIC);
2116: output.writeShort(STREAM_VERSION);
2117: }
2118:
2119: /**
2120: * Write primitive data of type String (<code>value</code>) in UTF
2121: * format into the receiver's underlying stream.
2122: *
2123: * @param value
2124: * The primitive data to write
2125: *
2126: * @throws IOException
2127: * If an IO exception happened when writing the primitive data.
2128: */
2129: public void writeUTF(String value) throws IOException {
2130: checkWritePrimitiveTypes();
2131: primitiveTypes.writeUTF(value);
2132: }
2133: }
|