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

+