Log Message
Merge solution for compiler dependent synthetic field names from trunk (XSTR-769).
Modified Paths
- branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java
- branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java
- branches/v-1.4.x/xstream-distribution/src/content/changes.html
Property Changed
Diff
Property changes: branches/v-1.4.x
Modified: svn:mergeinfo
Modified: branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java (2357 => 2358)
--- branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java 2015-02-24 01:17:13 UTC (rev 2357)
+++ branches/v-1.4.x/xstream/src/java/com/thoughtworks/xstream/mapper/OuterClassMapper.java 2015-02-24 18:40:47 UTC (rev 2358)
@@ -1,24 +1,34 @@
/*
* Copyright (C) 2005 Joe Walnes.
- * Copyright (C) 2006, 2007, 2009 XStream Committers.
+ * Copyright (C) 2006, 2007, 2009, 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+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 Map innerFields;
public OuterClassMapper(Mapper wrapped) {
this(wrapped, "outer-class");
@@ -27,21 +37,61 @@
public OuterClassMapper(Mapper wrapped, String alias) {
super(wrapped);
this.alias = alias;
+ innerFields = Collections.synchronizedMap(new HashMap());
+ innerFields.put(Object.class.getName(), EMPTY_NAMES);
}
public String serializedMember(Class type, 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);
}
public String realMember(Class type, 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 = (String[])innerFields.get(type.getName());
+ if (innerFieldNames == null) {
+ innerFieldNames = getInnerFieldNames(type.getSuperclass());
+ Field[] declaredFields = type.getDeclaredFields();
+ for (int i = 0; i < declaredFields.length; i++) {
+ final Field field = declaredFields[i];
+ if (field.getName().startsWith("this$")) {
+ String[] temp = new String[innerFieldNames.length+1];
+ System.arraycopy(innerFieldNames, 0, temp, 0, innerFieldNames.length);
+ innerFieldNames = temp;
+ innerFieldNames[innerFieldNames.length - 1] = field.getName();
+ }
+ }
+ innerFields.put(type.getName(), innerFieldNames);
+ }
+ return innerFieldNames;
+ }
+
+ public void flushCache() {
+ innerFields.keySet().retainAll(Collections.singletonList(Object.class.getName()));
+ }
}
Modified: branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java (2357 => 2358)
--- branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java 2015-02-24 01:17:13 UTC (rev 2357)
+++ branches/v-1.4.x/xstream/src/test/com/thoughtworks/acceptance/InnerClassesTest.java 2015-02-24 18:40:47 UTC (rev 2358)
@@ -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(new Class[]{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: branches/v-1.4.x/xstream-distribution/src/content/changes.html (2357 => 2358)
--- branches/v-1.4.x/xstream-distribution/src/content/changes.html 2015-02-24 01:17:13 UTC (rev 2357)
+++ branches/v-1.4.x/xstream-distribution/src/content/changes.html 2015-02-24 18:40:47 UTC (rev 2358)
@@ -35,6 +35,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:
