Title: [2357] trunk: Synthetic fields with references to outer class use compiler dependent names (XTSR-769).
Revision
2357
Author
joehni
Date
2015-02-23 19:17:13 -0600 (Mon, 23 Feb 2015)

Log Message

Synthetic fields with references to outer class use compiler dependent names (XTSR-769).

Modified Paths

Diff

Modified: trunk/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java (2356 => 2357)


--- trunk/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java	2015-02-20 15:33:04 UTC (rev 2356)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java	2015-02-24 01:17:13 UTC (rev 2357)
@@ -1,24 +1,35 @@
 /*
  * Copyright (C) 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2009, 2014 XStream Committers.
+ * Copyright (C) 2006, 2007, 2009, 2014, 2015 XStream Committers.
  * All rights reserved.
  *
  * 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 31. January 2005 by Joe Walnes
  */
 package com.thoughtworks.xstream.mapper;
 
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.thoughtworks.xstream.core.Caching;
+
+
 /**
  * Mapper that uses a more meaningful alias for the field in an inner class (this$0) that refers to the outer class.
- * 
+ *
  * @author Joe Walnes
  */
-public class OuterClassMapper extends MapperWrapper {
+public class OuterClassMapper extends MapperWrapper implements Caching {
 
+    private static final String[] EMPTY_NAMES = new String[0];
     private final String alias;
+    private final ConcurrentMap<String, String[]> innerFields;
 
     public OuterClassMapper(final Mapper wrapped) {
         this(wrapped, "outer-class");
@@ -27,23 +38,60 @@
     public OuterClassMapper(final Mapper wrapped, final String alias) {
         super(wrapped);
         this.alias = alias;
+        innerFields = new ConcurrentHashMap<String, String[]>();
+        innerFields.put(Object.class.getName(), EMPTY_NAMES);
     }
 
     @Override
     public String serializedMember(final Class<?> type, final String memberName) {
-        if (memberName.equals("this$0")) {
-            return alias;
-        } else {
-            return super.serializedMember(type, memberName);
+        if (memberName.startsWith("this$")) {
+            final String[] innerFieldNames = getInnerFieldNames(type);
+            for (int i = 0; i < innerFieldNames.length; ++i) {
+                if (innerFieldNames[i].equals(memberName)) {
+                    return i == 0 ? alias : alias + '-' + i;
+                }
+            }
         }
+        return super.serializedMember(type, memberName);
     }
 
     @Override
     public String realMember(final Class<?> type, final String serialized) {
-        if (serialized.equals(alias)) {
-            return "this$0";
-        } else {
-            return super.realMember(type, serialized);
+        if (serialized.startsWith(alias)) {
+            int idx = -1;
+            final int len = alias.length();
+            if (len == serialized.length()) {
+                idx = 0;
+            } else if (serialized.length() > len + 1 && serialized.charAt(len) == '-') {
+                idx = Integer.valueOf(serialized.substring(len + 1));
+            }
+            if (idx >= 0) {
+                final String[] innerFieldNames = getInnerFieldNames(type);
+                if (idx < innerFieldNames.length) {
+                    return innerFieldNames[idx];
+                }
+            }
         }
+        return super.realMember(type, serialized);
     }
+
+    private String[] getInnerFieldNames(final Class<?> type) {
+        String[] innerFieldNames = innerFields.get(type.getName());
+        if (innerFieldNames == null) {
+            innerFieldNames = getInnerFieldNames(type.getSuperclass());
+            for (final Field field : type.getDeclaredFields()) {
+                if (field.getName().startsWith("this$")) {
+                    innerFieldNames = Arrays.copyOf(innerFieldNames, innerFieldNames.length + 1);
+                    innerFieldNames[innerFieldNames.length - 1] = field.getName();
+                }
+            }
+            innerFields.putIfAbsent(type.getName(), innerFieldNames);
+        }
+        return innerFieldNames;
+    }
+
+    @Override
+    public void flushCache() {
+        innerFields.keySet().retainAll(Collections.singletonList(Object.class.getName()));
+    }
 }

Modified: trunk/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java (2356 => 2357)


--- trunk/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java	2015-02-20 15:33:04 UTC (rev 2356)
+++ trunk/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java	2015-02-24 01:17:13 UTC (rev 2357)
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2005, 2006 Joe Walnes.
- * Copyright (C) 2006, 2007, 2014 XStream Committers.
+ * Copyright (C) 2006, 2007, 2014, 2015 XStream Committers.
  * All rights reserved.
  *
  * 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 31. January 2005 by Joe Walnes
  */
 package com.thoughtworks.acceptance;
@@ -16,14 +16,14 @@
     public void testSerializedInnerClassMaintainsReferenceToOuterClass() {
         xstream.allowTypes(Outer.class, Outer.Inner.class);
 
-        Outer outer = new Outer("THE-OUTER-NAME", "THE-INNER-NAME");
-        Outer.Inner inner = outer.getInner();
+        final Outer outer = new Outer("THE-OUTER-NAME", "THE-INNER-NAME");
+        final Outer.Inner inner = outer.getInner();
 
         assertEquals("Hello from THE-INNER-NAME (inside THE-OUTER-NAME)", inner.getMessage());
 
-        String xml = xstream.toXML(inner);
+        final String xml = xstream.toXML(inner);
 
-        String expectedXml = ""
+        final String expectedXml = ""
                 + "<com.thoughtworks.acceptance.Outer_-Inner>\n"
                 + "  <innerName>THE-INNER-NAME</innerName>\n"
                 + "  <outer-class>\n"
@@ -33,18 +33,99 @@
                 + "</com.thoughtworks.acceptance.Outer_-Inner>";
         assertEquals(expectedXml, xml);
 
-        Outer.Inner newInner = (Outer.Inner) xstream.fromXML(xml);
+        final Outer.Inner newInner = (Outer.Inner)xstream.fromXML(xml);
 
         assertEquals("Hello from THE-INNER-NAME (inside THE-OUTER-NAME)", newInner.getMessage());
     }
+
+    public static class OuterType {
+        private final String outerName = "Outer Name";
+        public InnerType inner = new InnerType();
+        private final InnerType.Dynamic1 dyn1 = inner.new Dynamic1();
+        private final InnerType.Dynamic1.Dynamic2 dyn2 = dyn1.new Dynamic2();
+        private final InnerType.Dynamic3 dyn3 = inner.new Dynamic3(dyn1);
+
+        public class InnerType {
+            private final String innerName = "Inner Name";
+
+            public class Dynamic1 {
+                private final String name1 = "Name 1";
+
+                public class Dynamic2 {
+                    private final String name2 = "Name 2";
+                }
+            }
+
+            public class Dynamic3 extends Dynamic1.Dynamic2 {
+                private final String name3 = "Name 3";
+                private final Dynamic1.Dynamic2 dyn4;
+
+                public Dynamic3(final Dynamic1 outer) {
+                    outer.super();
+                    class Dynamic4 extends Dynamic1.Dynamic2 {
+                        private final String name4 = "Name 4";
+                        private final Dynamic5 dyn5 = new Dynamic5();
+                        class Dynamic5 {
+                            private final String name5 = "Name 5";
+                        }
+                        Dynamic4(Dynamic1 outer) {
+                            outer.super();
+                        }
+                    }
+                    dyn4 = new Dynamic4(outer);
+                }
+            }
+        }
+    }
+
+    public void testNestedDynamicTypes() {
+        xstream.alias("inner", OuterType.InnerType.class);
+
+        final OuterType outer = new OuterType();
+
+        final String expectedXml = ""
+                + "<inner>\n"
+                + "  <innerName>Inner Name</innerName>\n"
+                + "  <outer-class>\n"
+                + "    <outerName>Outer Name</outerName>\n"
+                + "    <inner reference=\"../..\"/>\n"
+                + "    <dyn1>\n"
+                + "      <name1>Name 1</name1>\n"
+                + "      <outer-class reference=\"../../..\"/>\n"
+                + "    </dyn1>\n"
+                + "    <dyn2>\n"
+                + "      <name2>Name 2</name2>\n"
+                + "      <outer-class reference=\"../../dyn1\"/>\n"
+                + "    </dyn2>\n"
+                + "    <dyn3>\n"
+                + "      <name2>Name 2</name2>\n"
+                + "      <outer-class reference=\"../../dyn1\"/>\n"
+                + "      <name3>Name 3</name3>\n"
+                + "      <dyn4 class=\"com.thoughtworks.acceptance.InnerClassesTest$OuterType$InnerType$Dynamic3$1Dynamic4\">\n"
+                + "        <name2>Name 2</name2>\n"
+                + "        <outer-class defined-in=\"com.thoughtworks.acceptance.InnerClassesTest$OuterType$InnerType$Dynamic1$Dynamic2\" reference=\"../../../dyn1\"/>\n"
+                + "        <name4>Name 4</name4>\n"
+                + "        <dyn5>\n"
+                + "          <name5>Name 5</name5>\n"
+                + "          <outer-class reference=\"../..\"/>\n"
+                + "        </dyn5>\n"
+                + "        <outer-class reference=\"../..\"/>\n"
+                + "      </dyn4>\n"
+                + "      <outer-class-1 reference=\"../../..\"/>\n"
+                + "    </dyn3>\n"
+                + "  </outer-class>\n"
+                + "</inner>";
+
+        assertBothWays(outer.inner, expectedXml);
+    }
 }
 
 class Outer {
 
-    private Inner inner;
-    private String outerName;
+    private final Inner inner;
+    private final String outerName;
 
-    public Outer(String outerName, String innerName) {
+    public Outer(final String outerName, final String innerName) {
         inner = new Inner(innerName);
         this.outerName = outerName;
     }
@@ -54,9 +135,9 @@
     }
 
     public class Inner {
-        private String innerName;
+        private final String innerName;
 
-        public Inner(String innerName) {
+        public Inner(final String innerName) {
             this.innerName = innerName;
         }
 
@@ -65,5 +146,3 @@
         }
     }
 }
-
-

Modified: trunk/xstream-distribution/src/content/changes.html (2356 => 2357)


--- trunk/xstream-distribution/src/content/changes.html	2015-02-20 15:33:04 UTC (rev 2356)
+++ trunk/xstream-distribution/src/content/changes.html	2015-02-24 01:17:13 UTC (rev 2357)
@@ -58,6 +58,7 @@
     <h2>Minor changes</h2>
 
     <ul>
+    	<li>JIRA:XSTR-769: Synthetic fields with references to outer class use compiler dependent names.</li>
     	<li>UUID is an immutable type by default.</li>
     </ul>
 

To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email

Reply via email to