This patch adds the missing java.io methods for 1.4,
which handle unshared object serialization.

Changelog:

2006-12-24  Andrew John Hughes  <[EMAIL PROTECTED]>

        * gnu/classpath/Pair.java: New class.
        * java/io/ObjectInputStream.java:
        (readUnshared()): Implemented.
        (readObject(boolean)): Renamed from readObject
        in order to handle unshared serialization.
        (parseContent(byte,boolean)): Added unshared parameter.
        (assignNewHandle(Object,boolean)): Likewise.
        (rememberHandle(Object,boolean,int)): Likewise.
        (lookupHandle(int)): Added handling of invalidated handles.
        (processResolution(ObjectStreamClass,Object,int,boolean)):
        Added unshared parameter.
        * java/io/ObjectOutputStream.java:
        (writeUnshared()): Implemented.
        (writeObject(Object,boolean)): Renamed from
        writeObject(Object) in order to handle
        unshared serialization.

-- 
Andrew :-)

Escape the Java Trap with GNU Classpath!
http://www.gnu.org/philosophy/java-trap.html
public class gcj extends Freedom implements Java { ... }
Index: gnu/classpath/Pair.java
===================================================================
RCS file: gnu/classpath/Pair.java
diff -N gnu/classpath/Pair.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/classpath/Pair.java     24 Dec 2006 20:36:20 -0000
@@ -0,0 +1,126 @@
+/* Pair.java -- A heterogenous pair of objects.
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.classpath;
+
+/**
+ * A container for a pair of heterogenous objects.
+ *
+ * @author Andrew John Hughes ([EMAIL PROTECTED])
+ */
+public class Pair<A,B>
+{
+
+  /**
+   * The left-hand side of the pair.
+   */
+  private A left;
+
+  /**
+   * The right-hand side of the pair.
+   */
+  private B right;
+
+  /**
+   * Constructs a new pair using the given left and
+   * right values.
+   *
+   * @param left the left-hand side of the pair.
+   * @param right the right-hand side of the pair.
+   */
+  public Pair(A left, B right)
+  {
+    this.left = left;
+    this.right = right;
+  }
+
+  /**
+   * Returns the left-hand side of the pair.
+   *
+   * @return the left-hand value.
+   */
+  public A getLeft()
+  {
+    return left;
+  }
+
+  /**
+   * Returns the right-hand side of the pair.
+   *
+   * @return the right-hand value.
+   */
+  public B getRight()
+  {
+    return right;
+  }
+
+  /**
+   * Returns true if the specified object is also a
+   * pair with equivalent left and right values.
+   *
+   * @param obj the object to compare.
+   * @return true if the two are equal.
+   */
+  public boolean equals(Object obj)
+  {
+    if (obj instanceof Pair)
+      {
+       Pair<A,B> p = (Pair<A,B>) obj;
+       A lp = p.getLeft();
+       B rp = p.getRight(); 
+       return (lp == null ? left == null : lp.equals(left)) &&
+         (rp == null ? right == null : rp.equals(right));
+      }
+    return false;
+  }
+
+  /**
+   * Returns a hashcode for the pair, created by the
+   * summation of the hashcodes of the left and right
+   * hand values.
+   *
+   * @return a hashcode for the pair.
+   */
+  public int hashCode()
+  {
+    return (left == null ? 0 : left.hashCode())
+      + (right == null ? 0 : right.hashCode());
+  }
+
+}
Index: java/io/ObjectInputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectInputStream.java,v
retrieving revision 1.80
diff -u -3 -p -u -r1.80 ObjectInputStream.java
--- java/io/ObjectInputStream.java      10 Dec 2006 20:25:44 -0000      1.80
+++ java/io/ObjectInputStream.java      24 Dec 2006 20:36:22 -0000
@@ -39,6 +39,7 @@ exception statement from your version. *
 
 package java.io;
 
+import gnu.classpath.Pair;
 import gnu.classpath.VMStackWalker;
 
 import java.lang.reflect.Array;
@@ -50,10 +51,11 @@ import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.TreeSet;
-import java.util.Vector;
 
 /**
  * @author Tom Tromey ([EMAIL PROTECTED])
@@ -104,7 +106,7 @@ public class ObjectInputStream extends I
     this.blockDataInput = new DataInputStream(this);
     this.realInputStream = new DataInputStream(in);
     this.nextOID = baseWireHandle;
-    this.objectLookupTable = new Vector<Object>();
+    handles = new HashMap<Integer,Pair<Boolean,Object>>();
     this.classLookupTable = new Hashtable<Class,ObjectStreamClass>();
     setBlockDataMode(true);
     readStreamHeader();
@@ -132,6 +134,70 @@ public class ObjectInputStream extends I
   public final Object readObject()
     throws ClassNotFoundException, IOException
   {
+    return readObject(true);
+  }
+
+  /**
+   * <p>
+   * Returns the next deserialized object read from the
+   * underlying stream in an unshared manner.  Any object
+   * returned by this method will not be returned by
+   * subsequent calls to either this method or [EMAIL PROTECTED] 
#readObject()}.
+   * </p>
+   * <p>
+   * This behaviour is achieved by:
+   * </p>
+   * <ul>
+   * <li>Marking the handles created by successful calls to this
+   * method, so that future calls to [EMAIL PROTECTED] #readObject()} or
+   * [EMAIL PROTECTED] #readUnshared()} will throw an [EMAIL PROTECTED] 
ObjectStreamException}
+   * rather than returning the same object reference.</li>
+   * <li>Throwing an [EMAIL PROTECTED] ObjectStreamException} if the next
+   * element in the stream is a reference to an earlier object.</li>
+   * </ul>
+   *
+   * @return a reference to the deserialized object.
+   * @throws ClassNotFoundException if the class of the object being
+   *                                deserialized can not be found.
+   * @throws StreamCorruptedException if information in the stream
+   *                                  is inconsistent.
+   * @throws ObjectStreamException if the next object has already been
+   *                               returned by an earlier call to this
+   *                               method or [EMAIL PROTECTED] #readObject()}.
+   * @throws OptionalDataException if primitive data occurs next in the stream.
+   * @throws IOException if an I/O error occurs from the stream.
+   * @since 1.4
+   * @see #readObject()
+   */
+  public Object readUnshared()
+    throws IOException, ClassNotFoundException
+  {
+    return readObject(false);
+  }
+
+  /**
+   * Returns the next deserialized object read from the underlying stream.
+   *
+   * This method can be overriden by a class by implementing
+   * <code>private void readObject (ObjectInputStream)</code>.
+   *
+   * If an exception is thrown from this method, the stream is left in
+   * an undefined state. This method can also throw Errors and 
+   * RuntimeExceptions if caused by existing readResolve() user code.
+   * 
+   * @param shared true if handles created by this call should be shared
+   *               with later calls.
+   * @return The object read from the underlying stream.
+   *
+   * @exception ClassNotFoundException The class that an object being
+   * read in belongs to cannot be found.
+   *
+   * @exception IOException Exception from underlying
+   * <code>InputStream</code>.
+   */
+  private final Object readObject(boolean shared)
+    throws ClassNotFoundException, IOException
+  {
     if (this.useSubclassMethod)
       return readObjectOverride();
 
@@ -146,7 +212,7 @@ public class ObjectInputStream extends I
 
     try
       {
-       ret_val = parseContent(marker);
+       ret_val = parseContent(marker, shared);
       }
     finally
       {
@@ -163,13 +229,15 @@ public class ObjectInputStream extends I
     * byte indicating its type.
     *
     * @param marker the byte marker.
+    * @param shared true if handles created by this call should be shared
+    *               with later calls.
     * @return an object which represents the parsed content.
     * @throws ClassNotFoundException if the class of an object being
     *                                read in cannot be found.
     * @throws IOException if invalid data occurs or one is thrown by the
     *                     underlying <code>InputStream</code>.
     */
-   private Object parseContent(byte marker)
+   private Object parseContent(byte marker, boolean shared)
      throws ClassNotFoundException, IOException
    {
      Object ret_val;
@@ -207,6 +275,9 @@ public class ObjectInputStream extends I
          int oid = realInputStream.readInt();
          if(dump) dumpElementln(Integer.toHexString(oid));
          ret_val = lookupHandle(oid);
+         if (!shared)
+           throw new
+             InvalidObjectException("References can not be read unshared.");
          break;
        }
        
@@ -215,7 +286,7 @@ public class ObjectInputStream extends I
          if(dump) dumpElementln("CLASS");
          ObjectStreamClass osc = (ObjectStreamClass)readObject();
          Class clazz = osc.forClass();
-         assignNewHandle(clazz);
+         assignNewHandle(clazz,shared);
          ret_val = clazz;
          break;
        }
@@ -250,7 +321,7 @@ public class ObjectInputStream extends I
                     new InternalError("Object ctor missing").initCause(x);
                 }
             }
-         assignNewHandle(osc);
+         assignNewHandle(osc,shared);
          
          if (!is_consumed)
            {
@@ -290,7 +361,8 @@ public class ObjectInputStream extends I
          if(dump) dumpElement("STRING=");
          String s = this.realInputStream.readUTF();
          if(dump) dumpElementln(s);
-         ret_val = processResolution(null, s, assignNewHandle(s));
+         ret_val = processResolution(null, s, assignNewHandle(s,shared),
+                                     shared);
          break;
        }
  
@@ -303,12 +375,12 @@ public class ObjectInputStream extends I
          int length = this.realInputStream.readInt();
          if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
          Object array = Array.newInstance(componentType, length);
-         int handle = assignNewHandle(array);
+         int handle = assignNewHandle(array,shared);
          readArrayElements(array, componentType);
          if(dump)
            for (int i = 0, len = Array.getLength(array); i < len; i++)
              dumpElementln("  ELEMENT[" + i + "]=" + Array.get(array, i));
-         ret_val = processResolution(null, array, handle);
+         ret_val = processResolution(null, array, handle, shared);
          break;
        }
        
@@ -326,7 +398,7 @@ public class ObjectInputStream extends I
            {
              Externalizable obj = osc.newInstance();
              
-             int handle = assignNewHandle(obj);
+             int handle = assignNewHandle(obj,shared);
              
              boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 
0);
              
@@ -344,14 +416,14 @@ public class ObjectInputStream extends I
                      throw new IOException("No end of block data seen for 
class with readExternal (ObjectInputStream) method.");
                }
 
-             ret_val = processResolution(osc, obj, handle);
+             ret_val = processResolution(osc, obj, handle,shared);
               break;
              
            } // end if (osc.realClassIsExternalizable)
          
          Object obj = newObject(clazz, 
osc.firstNonSerializableParentConstructor);
          
-         int handle = assignNewHandle(obj);
+         int handle = assignNewHandle(obj,shared);
          Object prevObject = this.currentObject;
          ObjectStreamClass prevObjectStreamClass = 
this.currentObjectStreamClass;
          TreeSet<ValidatorAndPriority> prevObjectValidators =
@@ -393,7 +465,7 @@ public class ObjectInputStream extends I
                      byte writeMarker = this.realInputStream.readByte();
                      while (writeMarker != TC_ENDBLOCKDATA)
                        {       
-                         parseContent(writeMarker);
+                         parseContent(writeMarker, shared);
                          writeMarker = this.realInputStream.readByte();
                        }
                      if(dump) dumpElementln("yes");
@@ -408,7 +480,7 @@ public class ObjectInputStream extends I
          
          this.currentObject = prevObject;
          this.currentObjectStreamClass = prevObjectStreamClass;
-         ret_val = processResolution(osc, obj, handle);
+         ret_val = processResolution(osc, obj, handle, shared);
          if (currentObjectValidators != null)
            invokeValidators();
          this.currentObjectValidators = prevObjectValidators;
@@ -442,7 +514,7 @@ public class ObjectInputStream extends I
             dumpElementln("CONSTANT NAME = " + constantName);
           Class clazz = osc.forClass();
           Enum instance = Enum.valueOf(clazz, constantName);
-          assignNewHandle(instance);
+          assignNewHandle(instance,shared);
           ret_val = instance;
           break;
         }
@@ -543,7 +615,7 @@ public class ObjectInputStream extends I
     ObjectStreamField[] fields = new ObjectStreamField[field_count];
     ObjectStreamClass osc = new ObjectStreamClass(name, uid,
                                                  flags, fields);
-    assignNewHandle(osc);
+    assignNewHandle(osc,true);
 
     for (int i = 0; i < field_count; i++)
       {
@@ -1544,13 +1616,15 @@ public class ObjectInputStream extends I
    * Assigns the next available handle to <code>obj</code>.
    *
    * @param obj The object for which we want a new handle.
+   * @param shared True if the handle should be shared
+   *               with later calls.
    * @return A valid handle for the specified object.
    */
-  private int assignNewHandle(Object obj)
+  private int assignNewHandle(Object obj, boolean shared)
   {
     int handle = this.nextOID;
     this.nextOID = handle + 1;
-    rememberHandle(obj,handle);
+    rememberHandle(obj,shared,handle);
     return handle;
   }
 
@@ -1558,40 +1632,44 @@ public class ObjectInputStream extends I
    * Remember the object associated with the given handle.
    *
    * @param obj an object
-   *
+   * @param shared true if the reference should be shared
+   *               with later calls.
    * @param handle a handle, must be >= baseWireHandle
    *
    * @see #lookupHandle
    */
-  private void rememberHandle(Object obj, int handle)
+  private void rememberHandle(Object obj, boolean shared,
+                             int handle)
   {
-    Vector olt = this.objectLookupTable;
-    handle = handle - baseWireHandle;
-
-    if (olt.size() <= handle)
-      olt.setSize(handle + 1);
-
-    olt.set(handle, obj);
+    handles.put(handle, new Pair<Boolean,Object>(shared, obj));
   }
   
   /**
    * Look up the object associated with a given handle.
    *
    * @param handle a handle, must be >= baseWireHandle
-   *
    * @return the object remembered for handle or null if none.
-   *
+   * @throws StreamCorruptedException if the handle is invalid.
+   * @throws InvalidObjectException if the reference is not shared.
    * @see #rememberHandle
    */
   private Object lookupHandle(int handle)
+    throws ObjectStreamException
   {
-    Vector olt = this.objectLookupTable;
-    handle = handle - baseWireHandle;
-    Object result = handle < olt.size() ? olt.get(handle) : null;
-    return result;
+    Pair<Boolean,Object> result = handles.get(handle);
+    if (result == null)
+      throw new StreamCorruptedException("The handle, " + 
+                                        Integer.toHexString(handle) +
+                                        ", is invalid.");
+    if (!result.getLeft())
+      throw new InvalidObjectException("The handle, " + 
+                                      Integer.toHexString(handle) +
+                                      ", is not shared.");
+    return result.getRight();
   }
 
-  private Object processResolution(ObjectStreamClass osc, Object obj, int 
handle)
+  private Object processResolution(ObjectStreamClass osc, Object obj, int 
handle,
+                                  boolean shared)
     throws IOException
   {
     if (osc != null && obj instanceof Serializable)
@@ -1622,13 +1700,34 @@ public class ObjectInputStream extends I
     if (this.resolveEnabled)
       obj = resolveObject(obj);
 
-    rememberHandle(obj, handle);
+    rememberHandle(obj, shared, handle);
+    if (!shared)
+      {
+       if (obj instanceof byte[])
+         return ((byte[]) obj).clone();
+       if (obj instanceof short[])
+         return ((short[]) obj).clone();
+       if (obj instanceof int[])
+         return ((int[]) obj).clone();
+       if (obj instanceof long[])
+         return ((long[]) obj).clone();
+       if (obj instanceof char[])
+         return ((char[]) obj).clone();
+       if (obj instanceof boolean[])
+         return ((boolean[]) obj).clone();
+       if (obj instanceof float[])
+         return ((float[]) obj).clone();
+       if (obj instanceof double[])
+         return ((double[]) obj).clone();
+       if (obj instanceof Object[])
+         return ((Object[]) obj).clone();
+      }
     return obj;
   }
 
   private void clearHandles()
   {
-    this.objectLookupTable.clear();
+    handles.clear();
     this.nextOID = baseWireHandle;
   }
 
@@ -1955,7 +2054,7 @@ public class ObjectInputStream extends I
   private boolean useSubclassMethod;
   private int nextOID;
   private boolean resolveEnabled;
-  private Vector<Object> objectLookupTable;
+  private Map<Integer,Pair<Boolean,Object>> handles;
   private Object currentObject;
   private ObjectStreamClass currentObjectStreamClass;
   private TreeSet<ValidatorAndPriority> currentObjectValidators;
Index: java/io/ObjectOutputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectOutputStream.java,v
retrieving revision 1.70
diff -u -3 -p -u -r1.70 ObjectOutputStream.java
--- java/io/ObjectOutputStream.java     10 Dec 2006 20:25:44 -0000      1.70
+++ java/io/ObjectOutputStream.java     24 Dec 2006 20:36:30 -0000
@@ -170,6 +170,7 @@ public class ObjectOutputStream extends 
    * If an exception is thrown from this method, the stream is left in
    * an undefined state.
    *
+   * @param obj the object to serialize.
    * @exception NotSerializableException An attempt was made to
    * serialize an <code>Object</code> that is not serializable.
    *
@@ -178,9 +179,71 @@ public class ObjectOutputStream extends 
    *
    * @exception IOException Exception from underlying
    * <code>OutputStream</code>.
+   * @see #writeUnshared(Object)
    */
   public final void writeObject(Object obj) throws IOException
   {
+    writeObject(obj, true);
+  }
+
+  /**
+   * Writes an object to the stream in the same manner as
+   * [EMAIL PROTECTED] #writeObject(Object)}, but without the use of
+   * references.  As a result, the object is always written
+   * to the stream in full.  Likewise, if an object is written
+   * by this method and is then later written again by
+   * [EMAIL PROTECTED] #writeObject(Object)}, both calls will write out
+   * the object in full, as the later call to
+   * [EMAIL PROTECTED] #writeObject(Object)} will know nothing of the
+   * earlier use of [EMAIL PROTECTED] #writeUnshared(Object)}.
+   *
+   * @param obj the object to serialize.
+   * @throws NotSerializableException if the object being
+   *                                  serialized does not implement
+   *                                  [EMAIL PROTECTED] Serializable}.
+   * @throws InvalidClassException if a problem occurs with
+   *                               the class of the object being
+   *                               serialized.
+   * @throws IOException if an I/O error occurs on the underlying
+   *                     <code>OutputStream</code>.
+   * @since 1.4
+   * @see #writeObject(Object)
+   */
+  public void writeUnshared(Object obj)
+    throws IOException
+  {
+    writeObject(obj, false);
+  }
+
+  /**
+   * Writes a representation of <code>obj</code> to the underlying
+   * output stream by writing out information about its class, then
+   * writing out each of the objects non-transient, non-static
+   * fields.  If any of these fields are other objects,
+   * they are written out in the same manner.
+   *
+   * This method can be overriden by a class by implementing
+   * <code>private void writeObject (ObjectOutputStream)</code>.
+   *
+   * If an exception is thrown from this method, the stream is left in
+   * an undefined state.
+   *
+   * @param obj the object to serialize.
+   * @param shared true if the serialized object should be
+   *               shared with later calls.
+   * @exception NotSerializableException An attempt was made to
+   * serialize an <code>Object</code> that is not serializable.
+   *
+   * @exception InvalidClassException Somebody tried to serialize
+   * an object which is wrongly formatted.
+   *
+   * @exception IOException Exception from underlying
+   * <code>OutputStream</code>.
+   * @see #writeUnshared(Object)
+   */
+  private final void writeObject(Object obj, boolean shared)
+    throws IOException
+  {
     if (useSubclassMethod)
       {
        if (dump)
@@ -212,7 +275,7 @@ public class ObjectOutputStream extends 
              }
 
            int handle = findHandle(obj);
-           if (handle >= 0)
+           if (handle >= 0 && shared)
              {
                realOutput.writeByte(TC_REFERENCE);
                realOutput.writeInt(handle);
@@ -243,7 +306,8 @@ public class ObjectOutputStream extends 
                    
                    writeObject(osc.getSuper());
                  }
-               assignNewHandle(obj);
+               if (shared)
+                 assignNewHandle(obj);
                break;
              }
 
@@ -263,7 +327,8 @@ public class ObjectOutputStream extends 
                /* TC_ENUM classDesc newHandle enumConstantName */
                realOutput.writeByte(TC_ENUM);
                writeObject(osc);
-               assignNewHandle(obj);
+               if (shared)
+                 assignNewHandle(obj);
                writeObject(((Enum) obj).name());
                break;
              }
@@ -299,7 +364,8 @@ public class ObjectOutputStream extends 
            if (obj instanceof String)
              {
                realOutput.writeByte(TC_STRING);
-               assignNewHandle(obj);
+               if (shared)
+                 assignNewHandle(obj);
                realOutput.writeUTF((String)obj);
                break;
              }
@@ -308,7 +374,8 @@ public class ObjectOutputStream extends 
              {
                realOutput.writeByte(TC_ARRAY);
                writeObject(osc);
-               assignNewHandle(obj);
+               if (shared)
+                 assignNewHandle(obj);
                writeArraySizeAndElements(obj, clazz.getComponentType());
                break;
              }
@@ -316,11 +383,12 @@ public class ObjectOutputStream extends 
            realOutput.writeByte(TC_OBJECT);
            writeObject(osc);
 
-           if (replaceDone)
-             assignNewHandle(replacedObject);
-           else
-             assignNewHandle(obj);
-
+           if (shared)
+             if (replaceDone)
+               assignNewHandle(replacedObject);
+             else
+               assignNewHandle(obj);
+           
            if (obj instanceof Externalizable)
              {
                if (protocolVersion == PROTOCOL_VERSION_2)

Attachment: signature.asc
Description: Digital signature

Reply via email to