Title: [2281] branches/v-1.4.x: Merge changes for XSTR-755 from trunk.

Diff

Property changes: branches/v-1.4.x


Modified: svn:mergeinfo

+ /trunk:2151-2152,2154-2156,2158-2163,2165,2172,2175,2177,2188-2189,2197,2199-2201,2204,2206,2210,2212,2214-2217,2226,2229,2231,2233-2234,2236-2238,2247-2249,2279

Modified: branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java (2280 => 2281)


--- branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java	2014-04-08 20:32:23 UTC (rev 2280)
+++ branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/converters/reflection/ExternalizableConverter.java	2014-04-08 20:43:59 UTC (rev 2281)
@@ -6,7 +6,7 @@
  * The software in this package is published under the terms of the BSD
  * style license a copy of which has been included with this distribution in
  * the LICENSE.txt file.
- * 
+ *
  * Created on 24. August 2004 by Joe Walnes
  */
 package com.thoughtworks.xstream.converters.reflection;
@@ -17,6 +17,7 @@
 import com.thoughtworks.xstream.converters.UnmarshallingContext;
 import com.thoughtworks.xstream.core.ClassLoaderReference;
 import com.thoughtworks.xstream.core.JVM;
+import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
 import com.thoughtworks.xstream.core.util.CustomObjectInputStream;
 import com.thoughtworks.xstream.core.util.CustomObjectOutputStream;
 import com.thoughtworks.xstream.core.util.HierarchicalStreams;
@@ -43,9 +44,11 @@
 
     private Mapper mapper;
     private final ClassLoaderReference classLoaderReference;
+    private transient SerializationMethodInvoker serializationMethodInvoker;
 
     /**
      * Construct an ExternalizableConverter.
+     *
      * @param mapper the Mapper chain
      * @param classLoaderReference the reference to XStream's {@link ClassLoader} instance
      * @since 1.4.5
@@ -53,6 +56,7 @@
     public ExternalizableConverter(Mapper mapper, ClassLoaderReference classLoaderReference) {
         this.mapper = mapper;
         this.classLoaderReference = classLoaderReference;
+        serializationMethodInvoker = new SerializationMethodInvoker();
     }
 
     /**
@@ -73,42 +77,54 @@
         return JVM.canCreateDerivedObjectOutputStream() && Externalizable.class.isAssignableFrom(type);
     }
 
-    public void marshal(Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
-        try {
-            Externalizable externalizable = (Externalizable) source;
-            CustomObjectOutputStream.StreamCallback callback = new CustomObjectOutputStream.StreamCallback() {
-                public void writeToStream(Object object) {
-                    if (object == null) {
-                        writer.startNode("null");
-                        writer.endNode();
-                    } else {
-                        ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper.serializedClass(object.getClass()), object.getClass());
-                        context.convertAnother(object);
-                        writer.endNode();
+    public void marshal(final Object original, final HierarchicalStreamWriter writer, final MarshallingContext context) {
+        final Object source = serializationMethodInvoker.callWriteReplace(original);
+        if (source != original && context instanceof ReferencingMarshallingContext) {
+            ((ReferencingMarshallingContext)context).replace(original, source);
+        }
+        if (source.getClass() != original.getClass()) {
+            final String attributeName = mapper.aliasForSystemAttribute("resolves-to");
+            if (attributeName != null) {
+                writer.addAttribute(attributeName, mapper.serializedClass(source.getClass()));
+            }
+            context.convertAnother(source);
+        } else {
+            try {
+                Externalizable externalizable = (Externalizable)source;
+                CustomObjectOutputStream.StreamCallback callback = new CustomObjectOutputStream.StreamCallback() {
+                    public void writeToStream(final Object object) {
+                        if (object == null) {
+                            writer.startNode("null");
+                            writer.endNode();
+                        } else {
+                            ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper.serializedClass(object.getClass()), object.getClass());
+                            context.convertAnother(object);
+                            writer.endNode();
+                        }
                     }
-                }
 
-                public void writeFieldsToStream(Map fields) {
-                    throw new UnsupportedOperationException();
-                }
+                    public void writeFieldsToStream(final Map fields) {
+                        throw new UnsupportedOperationException();
+                    }
 
-                public void defaultWriteObject() {
-                    throw new UnsupportedOperationException();
-                }
+                    public void defaultWriteObject() {
+                        throw new UnsupportedOperationException();
+                    }
 
-                public void flush() {
-                    writer.flush();
-                }
+                    public void flush() {
+                        writer.flush();
+                    }
 
-                public void close() {
-                    throw new UnsupportedOperationException("Objects are not allowed to call ObjectOutput.close() from writeExternal()");
-                }
-            };
-            CustomObjectOutputStream objectOutput = CustomObjectOutputStream.getInstance(context, callback);
-            externalizable.writeExternal(objectOutput);
-            objectOutput.popCallback();
-        } catch (IOException e) {
-            throw new ConversionException("Cannot serialize " + source.getClass().getName() + " using Externalization", e);
+                    public void close() {
+                        throw new UnsupportedOperationException("Objects are not allowed to call ObjectOutput.close() from writeExternal()");
+                    }
+                };
+                final CustomObjectOutputStream objectOutput = CustomObjectOutputStream.getInstance(context, callback);
+                externalizable.writeExternal(objectOutput);
+                objectOutput.popCallback();
+            } catch (IOException e) {
+                throw new ConversionException("Cannot serialize " + source.getClass().getName() + " using Externalization", e);
+            }
         }
     }
 
@@ -149,7 +165,7 @@
             CustomObjectInputStream objectInput = CustomObjectInputStream.getInstance(context, callback, classLoaderReference);
             externalizable.readExternal(objectInput);
             objectInput.popCallback();
-            return externalizable;
+            return serializationMethodInvoker.callReadResolve(externalizable);
         } catch (NoSuchMethodException e) {
             throw new ConversionException("Cannot construct " + type.getClass() + ", missing default constructor", e);
         } catch (InvocationTargetException e) {
@@ -164,4 +180,9 @@
             throw new ConversionException("Cannot externalize " + type.getClass(), e);
         }
     }
+
+    private Object readResolve() {
+        serializationMethodInvoker = new SerializationMethodInvoker();
+        return this;
+    }
 }

Modified: branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/WriteReplaceTest.java (2280 => 2281)


--- branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/WriteReplaceTest.java	2014-04-08 20:32:23 UTC (rev 2280)
+++ branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/WriteReplaceTest.java	2014-04-08 20:43:59 UTC (rev 2281)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2004, 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2014 XStream Committers.
  * All rights reserved.
  *
  * The software in this package is published under the terms of the BSD
@@ -27,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.lang.StringUtils;
+
 public class WriteReplaceTest extends AbstractAcceptanceTest {
 
     public static class Thing extends StandardObject implements Serializable {
@@ -283,4 +285,64 @@
 
         assertBothWays(in, expectedXml);
     }
+
+    public static class OriginalExternalizable extends StandardObject implements Externalizable {
+        String originalValue;
+
+        public OriginalExternalizable() {
+        }
+
+        public OriginalExternalizable(String originalValue) {
+            this.originalValue = originalValue;
+        }
+
+        private Object writeReplace() {
+            return new ReplacedExternalizable(originalValue.toUpperCase());
+        }
+
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeObject(originalValue);
+        }
+
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            originalValue = (String)in.readObject();
+        }
+    }
+
+    public static class ReplacedExternalizable extends StandardObject implements Externalizable {
+        String replacedValue;
+
+        public ReplacedExternalizable() {
+        }
+
+        public ReplacedExternalizable(String replacedValue) {
+            this.replacedValue = replacedValue;
+        }
+
+        private Object readResolve() {
+            return new OriginalExternalizable(replacedValue.toLowerCase());
+        }
+
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeObject(StringUtils.reverse(replacedValue));
+        }
+
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            replacedValue = StringUtils.reverse((String)in.readObject());
+        }
+    }
+
+    public void testAllowsDifferentTypeToBeSubstitutedForCustomExternalizableObjects() {
+        xstream.alias("original-externalizable-class", OriginalExternalizable.class);
+        xstream.alias("replaced-externalizable-class", ReplacedExternalizable.class);
+
+        OriginalExternalizable in = new OriginalExternalizable("hello world");
+
+        String expectedXml = ""
+                + "<original-externalizable-class resolves-to=\"replaced-externalizable-class\">\n"
+                + "  <string>DLROW OLLEH</string>\n"
+                + "</original-externalizable-class>";
+
+        assertBothWays(in, expectedXml);
+    }
 }

Modified: branches/v-1.4.x/xstream-distribution/src/content/changes.html (2280 => 2281)


--- branches/v-1.4.x/xstream-distribution/src/content/changes.html	2014-04-08 20:32:23 UTC (rev 2280)
+++ branches/v-1.4.x/xstream-distribution/src/content/changes.html	2014-04-08 20:43:59 UTC (rev 2281)
@@ -35,6 +35,7 @@
     <h2>Minor changes</h2>
     
     <ul>
+    	<li>XSTR-755: ExternalizableConverter does not respect writeReplace and readResolve.</li>
     	<li>Fix: DateConverter ignores provided locale.</li>
     	<li>Fix: WeakCache.entrySet().iterator().next.setValue(value) returns the reference instead of the old value.</li>
     	<li>Fix: SqlTimestampConverter throws IllegalArgumentException instead of ConversionException on fromString().</li>

To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email

Reply via email to