refactor Representer: introduce more protected methods to allow overriding
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
index 5ef904b..a4ddad5 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
@@ -49,7 +49,7 @@
this.representers.put(null, new RepresentJavaBean());
}
- private class RepresentJavaBean implements Represent {
+ protected class RepresentJavaBean implements Represent {
private final Map<Class<? extends Object>, Set<Property>> propertiesCache = new HashMap<Class<? extends Object>, Set<Property>>();
public Node representData(Object data) {
@@ -83,8 +83,7 @@
* instance for Node
* @return Node to get serialized
*/
- @SuppressWarnings("unchecked")
- private Node representJavaBean(Set<Property> properties, Object javaBean) {
+ protected Node representJavaBean(Set<Property> properties, Object javaBean) {
List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
String tag;
String customTag = classTags.get(javaBean.getClass());
@@ -118,60 +117,7 @@
}
// generic collections
if (nodeValue.getNodeId() != NodeId.scalar && !hasAlias) {
- Type[] arguments = property.getActualTypeArguments();
- if (arguments != null) {
- if (nodeValue.getNodeId() == NodeId.sequence) {
- // apply map tag where class is the same
- Class<? extends Object> t = (Class<? extends Object>) arguments[0];
- SequenceNode snode = (SequenceNode) nodeValue;
- List<Object> memberList = (List<Object>) memberValue;
- Iterator<Object> iter = memberList.iterator();
- for (Node childNode : snode.getValue()) {
- Object member = iter.next();
- if (t.equals(member.getClass())
- && childNode.getNodeId() == NodeId.mapping) {
- childNode.setTag(Tags.MAP);
- }
- }
-
- } else if (memberValue instanceof Set) {
- Class t = (Class) arguments[0];
- MappingNode mnode = (MappingNode) nodeValue;
- Iterator<NodeTuple> iter = mnode.getValue().iterator();
- Set set = (Set) memberValue;
- for (Object member : set) {
- NodeTuple tuple = iter.next();
- if (t.equals(member.getClass())
- && tuple.getKeyNode().getNodeId() == NodeId.mapping) {
- tuple.getKeyNode().setTag(Tags.MAP);
- }
- }
- } else if (nodeValue.getNodeId() == NodeId.mapping) {
- Class keyType = (Class) arguments[0];
- Class valueType = (Class) arguments[1];
- MappingNode mnode = (MappingNode) nodeValue;
- for (NodeTuple tuple : mnode.getValue()) {
- Node keyNode = tuple.getKeyNode();
- Node valueNode = tuple.getValueNode();
- String keyTag = keyNode.getTag();
- String valueTag = valueNode.getTag();
- if (Tags.getGlobalTagForClass(keyType).equals(keyTag)) {
- if (Enum.class.isAssignableFrom(keyType)) {
- keyNode.setTag(Tags.STR);
- } else {
- keyNode.setTag(Tags.MAP);
- }
- }
- if (Tags.getGlobalTagForClass(valueType).equals(valueTag)) {
- if (Enum.class.isAssignableFrom(valueType)) {
- valueNode.setTag(Tags.STR);
- } else {
- valueNode.setTag(Tags.MAP);
- }
- }
- }
- }
- }
+ checkGlobalTag(property, nodeValue, memberValue);
}
if (nodeKey.getStyle() != null) {
bestStyle = false;
@@ -189,7 +135,70 @@
return node;
}
- private Set<Property> getProperties(Class<? extends Object> type) throws IntrospectionException {
+ /**
+ * Remove redundant global tag for a type safe (generic) collection if it is
+ * the same as defined by the JavaBean property
+ *
+ * @param property
+ * - JavaBean property
+ * @param node
+ * - representation of the property
+ * @param object
+ * - instance represented by the node
+ */
+ @SuppressWarnings("unchecked")
+ protected void checkGlobalTag(Property property, Node node, Object object) {
+ Type[] arguments = property.getActualTypeArguments();
+ if (arguments != null) {
+ if (node.getNodeId() == NodeId.sequence) {
+ // apply map tag where class is the same
+ Class<? extends Object> t = (Class<? extends Object>) arguments[0];
+ SequenceNode snode = (SequenceNode) node;
+ List<Object> memberList = (List<Object>) object;
+ Iterator<Object> iter = memberList.iterator();
+ for (Node childNode : snode.getValue()) {
+ Object member = iter.next();
+ if (t.equals(member.getClass()) && childNode.getNodeId() == NodeId.mapping) {
+ childNode.setTag(Tags.MAP);
+ }
+ }
+ } else if (object instanceof Set) {
+ Class t = (Class) arguments[0];
+ MappingNode mnode = (MappingNode) node;
+ Iterator<NodeTuple> iter = mnode.getValue().iterator();
+ Set set = (Set) object;
+ for (Object member : set) {
+ NodeTuple tuple = iter.next();
+ if (t.equals(member.getClass())
+ && tuple.getKeyNode().getNodeId() == NodeId.mapping) {
+ tuple.getKeyNode().setTag(Tags.MAP);
+ }
+ }
+ } else if (node.getNodeId() == NodeId.mapping) {
+ Class keyType = (Class) arguments[0];
+ Class valueType = (Class) arguments[1];
+ MappingNode mnode = (MappingNode) node;
+ for (NodeTuple tuple : mnode.getValue()) {
+ resetTag(keyType, tuple.getKeyNode());
+ resetTag(valueType, tuple.getValueNode());
+ }
+ }
+ }
+ }
+
+ private void resetTag(Class<? extends Object> type, Node node) {
+ String tag = node.getTag();
+ if (Tags.getGlobalTagForClass(type).equals(tag)) {
+ if (Enum.class.isAssignableFrom(type)) {
+ node.setTag(Tags.STR);
+ } else {
+ node.setTag(Tags.MAP);
+ }
+ }
+ }
+
+ protected Set<Property> getProperties(Class<? extends Object> type)
+ throws IntrospectionException {
Set<Property> properties = new TreeSet<Property>();
// add JavaBean getters
for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors())
diff --git a/src/test/java/examples/collections/TypeSafeMap2Test.java b/src/test/java/examples/collections/TypeSafeMap2Test.java
index bbf78a7..9ce3ca1 100644
--- a/src/test/java/examples/collections/TypeSafeMap2Test.java
+++ b/src/test/java/examples/collections/TypeSafeMap2Test.java
@@ -47,6 +47,37 @@
assertEquals(etalon, output);
}
+ public void testMap2() {
+ MapBean2 bean = new MapBean2();
+ Map<Developer2, Color> data = new LinkedHashMap<Developer2, Color>();
+ data.put(new Developer2("Andy", "tester"), Color.BLACK);
+ data.put(new SuperMan("Bill", "cleaner", false), Color.BLACK);
+ data.put(new Developer2("Lisa", "owner"), Color.RED);
+ bean.setData(data);
+ Map<Color, Developer2> developers = new LinkedHashMap<Color, Developer2>();
+ developers.put(Color.WHITE, new Developer2("Fred", "creator"));
+ developers.put(Color.RED, new SuperMan("Jason", "contributor", true));
+ developers.put(Color.BLACK, new Developer2("John", "committer"));
+ bean.setDevelopers(developers);
+ JavaBeanDumper dumper = new JavaBeanDumper(false);
+ String output = dumper.dump(bean);
+ // System.out.println(output);
+ String etalon = Util.getLocalResource("examples/map-bean-13.yaml");
+ assertEquals(etalon, output);
+ // load
+ JavaBeanLoader<MapBean2> beanLoader = new JavaBeanLoader<MapBean2>(MapBean2.class);
+ MapBean2 parsed = beanLoader.load(etalon);
+ assertNotNull(parsed);
+ Map<Developer2, Color> parsedData = parsed.getData();
+ assertEquals(3, parsedData.size());
+ assertTrue(parsedData.containsKey(new SuperMan("Bill", "cleaner", false)));
+ assertEquals(Color.BLACK, parsedData.get(new SuperMan("Bill", "cleaner", false)));
+ //
+ Map<Color, Developer2> parsedDevelopers = parsed.getDevelopers();
+ assertEquals(3, parsedDevelopers.size());
+ assertEquals(new SuperMan("Jason", "contributor", true), parsedDevelopers.get(Color.RED));
+ }
+
public void testLoadMap() {
String output = Util.getLocalResource("examples/map-bean-12.yaml");
// System.out.println(output);
@@ -126,7 +157,7 @@
public Developer2() {
}
- public Developer2(String name, String role) {
+ private Developer2(String name, String role) {
this.name = name;
this.role = role;
}
@@ -150,6 +181,51 @@
public int compareTo(Developer2 o) {
return name.compareTo(o.name);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Developer2) {
+ return toString().equals(obj.toString());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Developer " + name + " " + role;
+ }
+
}
+ public static class SuperMan extends Developer2 {
+ private boolean smart;
+
+ public SuperMan() {
+ super();
+ }
+
+ private SuperMan(String name, String role, boolean smart) {
+ super(name, role);
+ this.smart = smart;
+ }
+
+ public boolean isSmart() {
+ return smart;
+ }
+
+ public void setSmart(boolean smart) {
+ this.smart = smart;
+ }
+
+ @Override
+ public String toString() {
+ return "Super" + super.toString();
+ }
+ }
}
diff --git a/src/test/resources/examples/map-bean-13.yaml b/src/test/resources/examples/map-bean-13.yaml
new file mode 100644
index 0000000..0c2e3ea
--- /dev/null
+++ b/src/test/resources/examples/map-bean-13.yaml
@@ -0,0 +1,25 @@
+data:
+ ? name: Andy
+ role: tester
+ : BLACK
+ ? !!examples.collections.TypeSafeMap2Test$SuperMan
+ name: Bill
+ role: cleaner
+ smart: false
+ : BLACK
+ ? name: Lisa
+ role: owner
+ : RED
+developers:
+ WHITE:
+ name: Fred
+ role: creator
+ RED: !!examples.collections.TypeSafeMap2Test$SuperMan
+ name: Jason
+ role: contributor
+ smart: true
+ BLACK:
+ name: John
+ role: committer
+name: Bean123
+