apply recursive_4.patch.txt from Alexander Maslov (issue 1)
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
index 1460610..c72531b 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -6,6 +6,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -154,6 +155,17 @@
         return new LinkedHashMap<Object, Object>();
     }
 
+    protected Set<Object> createDefaultSet() {
+        // respect order from YAML document
+        return new LinkedHashSet<Object>();
+    }
+
+    protected Set<Object> constructSet(MappingNode node) {
+        Set<Object> set = createDefaultSet();
+        constructSet2ndStep(node, set);
+        return set;
+    }
+
     protected Map<Object, Object> constructMapping(MappingNode node) {
         Map<Object, Object> mapping = createDefaultMap();
         constructMapping2ndStep(node, mapping);
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index cfe9878..180b3dc 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -96,27 +96,16 @@
         @SuppressWarnings("unchecked")
         public Object construct(Node node) {
             Object result = null;
-            Class<? extends Object> customTag = typeTags.get(node.getTag());
             try {
-                Class cl;
-                if (customTag == null) {
-                    if (node.getTag().length() < "tag:yaml.org,2002:".length()) {
-                        throw new YAMLException("Unknown tag: " + node.getTag());
-                    }
-                    String name = node.getTag().substring("tag:yaml.org,2002:".length());
-                    cl = Class.forName(name);
-                } else {
-                    cl = customTag;
-                }
+                Class cl = getClassForNode(node);
                 java.lang.reflect.Constructor javaConstructor;
                 switch (node.getNodeId()) {
                 case mapping:
-                    MappingNode mnode = (MappingNode) node;
-                    mnode.setType(cl);
+                    node.setType(cl);
                     if (node.isTwoStepsConstruction()) {
-                        result = createMappingNode(cl);
+                        result = createMappingNode(node);
                     } else {
-                        result = constructMappingNode(mnode);
+                        result = constructMappingNode((MappingNode) node);
                     }
                     break;
                 case sequence:
@@ -167,14 +156,29 @@
             result = constructScalarNode((ScalarNode) node);
             break;
         case sequence:
-            result = constructSequence((SequenceNode) node);
+            SequenceNode snode = (SequenceNode) node;
+            if (node.isTwoStepsConstruction()) {
+                result = createDefaultList(snode.getValue().size());
+            } else {
+                result = constructSequence(snode);
+            }
             break;
         default:// mapping
             if (Map.class.isAssignableFrom(node.getType())) {
-                result = super.constructMapping((MappingNode) node);
+                if (node.isTwoStepsConstruction()) {
+                    result = createDefaultMap();
+                } else {
+                    result = constructMapping((MappingNode) node);
+                }
+            } else if (Set.class.isAssignableFrom(node.getType())) {
+                if (node.isTwoStepsConstruction()) {
+                    result = createDefaultSet();
+                } else {
+                    result = constructSet((MappingNode) node);
+                }
             } else {
                 if (node.isTwoStepsConstruction()) {
-                    result = createMappingNode(node.getType());
+                    result = createMappingNode(node);
                 } else {
                     result = constructMappingNode((MappingNode) node);
                 }
@@ -204,7 +208,7 @@
                 } else if (Set.class.isAssignableFrom(node.getType())) {
                     constructSet2ndStep((MappingNode) node, (Set<Object>) object);
                 } else {
-                    constructMappingNode2ndStep((MappingNode) node, object, node.getType());
+                    constructMappingNode2ndStep((MappingNode) node, object);
                 }
             }
         }
@@ -295,8 +299,12 @@
         return result;
     }
 
-    private Object createMappingNode(Class<?> beanType) {
+    private Object createMappingNode(Node node) {
         try {
+            Class<? extends Object> type = node.getType();
+            if (Modifier.isAbstract(type.getModifiers())) {
+                node.setType(getClassForNode(node));
+            }
             /**
              * Using only default constructor. Everything else will be
              * initialized on 2nd step. If we do here some partial
@@ -304,11 +312,13 @@
              * step? I think it is better to get only object here (to have it as
              * reference for recursion) and do all other thing on 2nd step.
              */
-            return beanType.newInstance();
+            return node.getType().newInstance();
         } catch (InstantiationException e) {
             throw new YAMLException(e);
         } catch (IllegalAccessException e) {
             throw new YAMLException(e);
+        } catch (ClassNotFoundException e) {
+            throw new YAMLException(e);
         }
     }
 
@@ -322,13 +332,12 @@
      * @return constructed JavaBean
      */
     private Object constructMappingNode(MappingNode node) {
-        Class<? extends Object> beanType = node.getType();
-        return constructMappingNode2ndStep(node, createMappingNode(beanType), beanType);
+        return constructMappingNode2ndStep(node, createMappingNode(node));
     }
 
     @SuppressWarnings("unchecked")
-    private Object constructMappingNode2ndStep(MappingNode node, Object object,
-            Class<? extends Object> beanType) {
+    private Object constructMappingNode2ndStep(MappingNode node, Object object) {
+        Class<? extends Object> beanType = node.getType();
         List<Node[]> nodeValue = (List<Node[]>) node.getValue();
         for (Node[] tuple : nodeValue) {
             ScalarNode keyNode;
@@ -406,9 +415,25 @@
             }
             if (field.getName().equals(name)) {
                 return new FieldProperty(field);
-        }
+            }
         }
         throw new YAMLException("Unable to find property '" + name + "' on class: "
                 + type.getName());
     }
+
+    protected Class<?> getClassForNode(Node node) throws ClassNotFoundException {
+        Class<? extends Object> customTag = typeTags.get(node.getTag());
+        if (customTag == null) {
+            if (node.getTag().length() < "tag:yaml.org,2002:".length()) {
+                throw new YAMLException("Unknown tag: " + node.getTag());
+            }
+            String name = node.getTag().substring("tag:yaml.org,2002:".length());
+            Class<?> cl = Class.forName(name);
+            typeTags.put(node.getTag(), cl);
+            return cl;
+        } else {
+            return customTag;
+        }
+    }
+
 }
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
index 7ea30c4..6a0de9a 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
@@ -99,11 +99,6 @@
         }

     }

 

-    protected Map<Object, Object> constructMapping(MappingNode node) {

-        flattenMapping(node);

-        return super.constructMapping(node);

-    }

-

     protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {

         flattenMapping(node);

         super.constructMapping2ndStep(node, mapping);

@@ -378,9 +373,9 @@
     private class ConstuctYamlSet extends AbstractConstruct {

         public Object construct(Node node) {

             if (node.isTwoStepsConstruction()) {

-                return createDefaultMap().keySet();

+                return createDefaultSet();

             } else {

-                return constructMapping((MappingNode) node).keySet();

+                return constructSet((MappingNode) node);

             }

         }

 

@@ -405,10 +400,8 @@
         @SuppressWarnings("unchecked")

         public Object construct(Node node) {

             if (node.isTwoStepsConstruction()) {

-                List<Node> nodeValue = (List<Node>) node.getValue();

-                return createDefaultList(nodeValue.size());

+                return createDefaultList(((List<Node>) node.getValue()).size());

             } else {

-                // TODO is it ever called ???

                 return constructSequence((SequenceNode) node);

             }

         }

diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java b/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java
index e7c77a8..1a8599f 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java
@@ -9,9 +9,6 @@
     private final K _2;
 
     public Tuple(T _1, K _2) {
-        if (_1 == null || _2 == null) {
-            throw new NullPointerException("Tuple does not accept NULL.");
-        }
         this._1 = _1;
         this._2 = _2;
     }
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java b/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java
new file mode 100644
index 0000000..6d9cc3e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java
@@ -0,0 +1,37 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive;

+

+import java.util.Date;

+

+public abstract class AbstractHuman {

+    private String name;

+    private Date birthday;

+    private String birthPlace;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public Date getBirthday() {

+        return birthday;

+    }

+

+    public void setBirthday(Date birthday) {

+        this.birthday = birthday;

+    }

+

+    public String getBirthPlace() {

+        return birthPlace;

+    }

+

+    public void setBirthPlace(String birthPlace) {

+        this.birthPlace = birthPlace;

+    }

+    

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human.java b/src/test/java/org/yaml/snakeyaml/recursive/Human.java
index 8df0e54..bd6fe99 100644
--- a/src/test/java/org/yaml/snakeyaml/recursive/Human.java
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human.java
@@ -3,44 +3,21 @@
  */

 package org.yaml.snakeyaml.recursive;

 

-import java.util.Date;

-import java.util.LinkedList;

-import java.util.List;

+import java.util.LinkedHashSet;

+import java.util.Set;

 

-public class Human {

-    private String name;

-    private Date birthday;

-    private String birthPlace;

+public class Human extends AbstractHuman {

+    

     private Human father;

     private Human mother;

     private Human parner;

     private Human bankAccountOwner;

-    private List<Human> children;

-

+    protected Set<Human> children;

+    

     public Human() {

-        children = new LinkedList<Human>();

+        children = new LinkedHashSet<Human>();

     }

-

-    public String getName() {

-        return name;

-    }

-

-    public void setName(String name) {

-        this.name = name;

-    }

-

-    public Date getBirthday() {

-        return birthday;

-    }

-

-    public void setBirthday(Date birthday) {

-        this.birthday = birthday;

-    }

-

-    public String getBirthPlace() {

-        return birthPlace;

-    }

-

+    

     public Human getFather() {

         return father;

     }

@@ -57,18 +34,6 @@
         this.mother = mother;

     }

 

-    public void setBirthPlace(String birthPlace) {

-        this.birthPlace = birthPlace;

-    }

-

-    public List<Human> getChildren() {

-        return children;

-    }

-

-    public void setChildren(List<Human> children) {

-        this.children = children;

-    }

-

     public Human getParner() {

         return parner;

     }

@@ -84,4 +49,13 @@
     public void setBankAccountOwner(Human bankAccountOwner) {

         this.bankAccountOwner = bankAccountOwner;

     }

+

+    public Set<Human> getChildren() {

+        return children;

+    }

+

+    public void setChildren(Set<Human> children) {

+        this.children = children;

+    }

+

 }

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human2.java b/src/test/java/org/yaml/snakeyaml/recursive/Human2.java
new file mode 100644
index 0000000..4319f94
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human2.java
@@ -0,0 +1,61 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive;

+

+import java.util.HashMap;

+import java.util.Map;

+

+public class Human2 extends AbstractHuman {

+

+    private Human2 father;

+    private Human2 mother;

+    private Human2 parner;

+    private Human2 bankAccountOwner;

+    protected Map<Human2, String> children;

+    

+    public Human2() {

+        children = new HashMap<Human2, String>();

+    }

+

+    public Human2 getFather() {

+        return father;

+    }

+

+    public void setFather(Human2 father) {

+        this.father = father;

+    }

+

+    public Human2 getMother() {

+        return mother;

+    }

+

+    public void setMother(Human2 mother) {

+        this.mother = mother;

+    }

+

+    public Human2 getParner() {

+        return parner;

+    }

+

+    public void setParner(Human2 parner) {

+        this.parner = parner;

+    }

+

+    public Human2 getBankAccountOwner() {

+        return bankAccountOwner;

+    }

+

+    public void setBankAccountOwner(Human2 bankAccountOwner) {

+        this.bankAccountOwner = bankAccountOwner;

+    }

+

+    public Map<Human2, String> getChildren() {

+        return children;

+    }

+

+    public void setChildren(Map<Human2, String> children) {

+        this.children = children;

+    }

+    

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human3.java b/src/test/java/org/yaml/snakeyaml/recursive/Human3.java
new file mode 100644
index 0000000..2d87698
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human3.java
@@ -0,0 +1,61 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive;

+

+import java.util.LinkedList;

+import java.util.List;

+

+public class Human3 extends AbstractHuman {

+

+    private Human3 father;

+    private Human3 mother;

+    private Human3 parner;

+    private Human3 bankAccountOwner;

+    protected List<Human3> children;

+    

+    public Human3() {

+        children = new LinkedList<Human3>();

+    }

+

+    public Human3 getFather() {

+        return father;

+    }

+

+    public void setFather(Human3 father) {

+        this.father = father;

+    }

+

+    public Human3 getMother() {

+        return mother;

+    }

+

+    public void setMother(Human3 mother) {

+        this.mother = mother;

+    }

+

+    public Human3 getParner() {

+        return parner;

+    }

+

+    public void setParner(Human3 parner) {

+        this.parner = parner;

+    }

+

+    public Human3 getBankAccountOwner() {

+        return bankAccountOwner;

+    }

+

+    public void setBankAccountOwner(Human3 bankAccountOwner) {

+        this.bankAccountOwner = bankAccountOwner;

+    }

+

+    public List<Human3> getChildren() {

+        return children;

+    }

+

+    public void setChildren(List<Human3> children) {

+        this.children = children;

+    }

+    

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java b/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java
index a7e6b92..8356f93 100644
--- a/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java
+++ b/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java
@@ -4,13 +4,15 @@
 package org.yaml.snakeyaml.recursive;

 

 import java.io.IOException;

-import java.util.ArrayList;

 import java.util.Date;

 import java.util.HashMap;

-import java.util.HashSet;

+import java.util.LinkedHashMap;

+import java.util.LinkedHashSet;

+import java.util.LinkedList;

 import java.util.List;

 import java.util.Map;

 import java.util.Set;

+import java.util.Map.Entry;

 

 import junit.framework.TestCase;

 

@@ -79,7 +81,7 @@
         daughter.setFather(father);

         daughter.setMother(mother);

         //

-        List<Human> children = new ArrayList<Human>(2);

+        Set<Human> children = new LinkedHashSet<Human>(2);

         children.add(son);

         children.add(daughter);

         father.setChildren(children);

@@ -88,21 +90,312 @@
 

         Constructor constructor = new Constructor();

         TypeDescription humanDescription = new TypeDescription(Human.class);

-        humanDescription.putListPropertyType("children", Human.class);

+        humanDescription.putMapPropertyType("children", Human.class, Object.class);

         constructor.addTypeDescription(humanDescription);

 

         Yaml yaml = new Yaml(new Loader(constructor));

-        String output = yaml.dump(father);

-        System.out.println(output);

+        String output = yaml.dump(son);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/with-children.yaml");

+        assertEquals(etalon, output);

+        //

+        Human son2 = (Human) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        Set<Human> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human.class, child.getClass()); // check if type

+                                                       // descriptor was correct

+        }

+    }

+

+    public void testChildren2() throws IOException {

+        Human2 father = new Human2();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human2 mother = new Human2();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human2 son = new Human2();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human2 daughter = new Human2();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        HashMap<Human2, String> children = new LinkedHashMap<Human2, String>(2);

+        children.put(son, "son");

+        children.put(daughter, "daughter");

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription humanDescription = new TypeDescription(Human2.class);

+        humanDescription.putMapPropertyType("children", Human2.class, String.class);

+        constructor.addTypeDescription(humanDescription);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(son);

+        // System.out.println(output);

         String etalon = Util.getLocalResource("recursive/with-children-2.yaml");

         assertEquals(etalon, output);

         //

-        Human father2 = (Human) yaml.load(output);

-        assertNotNull(father2);

+        Human2 son2 = (Human2) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human2 father2 = son2.getFather();

         assertEquals("Father", father2.getName());

-        assertEquals("Mother", father2.getParner().getName());

-        assertEquals("Father", father2.getBankAccountOwner().getName());

+        assertEquals("Mother", son2.getMother().getName());

         assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        Map<Human2, String> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+    }

+

+    public void testChildren3() throws IOException {

+        Human3 father = new Human3();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human3 mother = new Human3();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human3 son = new Human3();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human3 daughter = new Human3();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        LinkedList<Human3> children = new LinkedList<Human3>();

+        children.add(son);

+        children.add(daughter);

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human3Description = new TypeDescription(Human3.class);

+        Human3Description.putListPropertyType("children", Human3.class);

+        constructor.addTypeDescription(Human3Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(son);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/with-children-3.yaml");

+        assertEquals(etalon, output);

+        //

+        Human3 son2 = (Human3) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human3 father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        List<Human3> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human3.class, child.getClass()); // check if type

+                                                        // descriptor was

+                                                        // correct

+        }

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is set

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenSetAsRoot() throws IOException {

+        String etalon = Util.getLocalResource("recursive/with-children-as-set.yaml");

+

+        Constructor constructor = new Constructor();

+        TypeDescription humanDescription = new TypeDescription(Human.class);

+        humanDescription.putMapPropertyType("children", Human.class, Object.class);

+        constructor.addTypeDescription(humanDescription);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        Set<Human> children2 = (Set<Human>) yaml.load(etalon);

+        assertNotNull(children2);

+        assertEquals(2, children2.size());

+

+        Human firstChild = children2.iterator().next();

+

+        Human father2 = firstChild.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", firstChild.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), firstChild.getMother());

+        assertSame(father2, firstChild.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human.class, child.getClass()); // check if type

+                                                       // descriptor was correct

+        }

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is map

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenMapAsRoot() throws IOException {

+        String etalon = Util.getLocalResource("recursive/with-children-as-map.yaml");

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human2Description = new TypeDescription(Human2.class);

+        Human2Description.putMapPropertyType("children", Human2.class, String.class);

+        constructor.addTypeDescription(Human2Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        Map<Human2, String> children2 = (Map<Human2, String>) yaml.load(etalon);

+        assertNotNull(children2);

+        assertEquals(2, children2.size());

+

+        Entry<Human2, String> firstEntry = children2.entrySet().iterator().next();

+        Human2 firstChild = firstEntry.getKey();

+

+        Human2 father2 = firstChild.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", firstChild.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), firstChild.getMother());

+        assertSame(father2, firstChild.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is list

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenListRoot() throws IOException {

+        Human3 father = new Human3();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human3 mother = new Human3();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human3 son = new Human3();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human3 daughter = new Human3();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        LinkedList<Human3> children = new LinkedList<Human3>();

+        children.add(son);

+        children.add(daughter);

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human3Description = new TypeDescription(Human3.class);

+        Human3Description.putListPropertyType("children", Human3.class);

+        constructor.addTypeDescription(Human3Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(father.getChildren());

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/with-children-as-list.yaml");

+        assertEquals(etalon, output);

+        //

+        List<Human3> children2 = (List<Human3>) yaml.load(output);

+        assertNotNull(children2);

+        Human3 son2 = children2.iterator().next();

+        assertEquals(2, children2.size());

+

+        Human3 father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human3.class, child.getClass()); // check if type

+                                                        // descriptor was

+                                                        // correct

+        }

     }

 

     public void testBeanRing() throws IOException {

@@ -118,7 +411,7 @@
         //

         Yaml yaml = new Yaml();

         String output = yaml.dump(man1);

-        System.out.println(output);

+        // System.out.println(output);

         String etalon = Util.getLocalResource("recursive/beanring-3.yaml");

         assertEquals(etalon, output);

         //

@@ -132,22 +425,22 @@
 

     // TODO Java's hashcode leaves much to be desired

     public void qtestCollectionRing() throws IOException {

-        Set<Object> set = new HashSet<Object>();

-        List<Object> list = new ArrayList<Object>();

-        Map<Object, Object> map = new HashMap<Object, Object>();

-        set.add(list);

-        list.add(map);

-        map.put("1", set);

-        //

-        try {

-            Yaml yaml = new Yaml();

-            String output = yaml.dump(set);

-            // String etalon = Util.getLocalResource("recursive/???.yaml");

-            // assertEquals(etalon, output);

-            //

-            // Set<Object> loadedSet = (Set<Object>) yaml.load(output);

-        } catch (StackOverflowError e) {

-            fail("Cannot dump recursive collections.");

-        }

+        // Set<Object> set = new HashSet<Object>();

+        // List<Object> list = new ArrayList<Object>();

+        // Map<Object, Object> map = new HashMap<Object, Object>();

+        // set.add(list);

+        // list.add(map);

+        // map.put("1", set);

+        // //

+        // try {

+        // Yaml yaml = new Yaml();

+        // String output = yaml.dump(set);

+        // // String etalon = Util.getLocalResource("recursive/???.yaml");

+        // // assertEquals(etalon, output);

+        // //

+        // // Set<Object> loadedSet = (Set<Object>) yaml.load(output);

+        // } catch (StackOverflowError e) {

+        // fail("Cannot dump recursive collections.");

+        // }

     }

 }

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHuman.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHuman.java
new file mode 100644
index 0000000..d244591
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHuman.java
@@ -0,0 +1,82 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive.generics;

+

+import java.util.Date;

+

+public abstract class AbstractHuman<T, K extends AbstractHuman<T, ?>> {

+    private String name;

+    private Date birthday;

+    private String birthPlace;

+    private K father;

+    private K mother;

+    private K parner;

+    private K bankAccountOwner;

+    protected T children;

+

+    public String getName() {

+        return name;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public Date getBirthday() {

+        return birthday;

+    }

+

+    public void setBirthday(Date birthday) {

+        this.birthday = birthday;

+    }

+

+    public String getBirthPlace() {

+        return birthPlace;

+    }

+

+    public K getFather() {

+        return father;

+    }

+

+    public void setFather(K father) {

+        this.father = father;

+    }

+

+    public K getMother() {

+        return mother;

+    }

+

+    public void setMother(K mother) {

+        this.mother = mother;

+    }

+

+    public void setBirthPlace(String birthPlace) {

+        this.birthPlace = birthPlace;

+    }

+

+    public T getChildren() {

+        return children;

+    }

+

+    public void setChildren(T children) {

+        this.children = children;

+    }

+

+    public K getParner() {

+        return parner;

+    }

+

+    public void setParner(K parner) {

+        this.parner = parner;

+    }

+

+    public K getBankAccountOwner() {

+        return bankAccountOwner;

+    }

+

+    public void setBankAccountOwner(K bankAccountOwner) {

+        this.bankAccountOwner = bankAccountOwner;

+    }

+    

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/Human.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human.java
new file mode 100644
index 0000000..339acab
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human.java
@@ -0,0 +1,13 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive.generics;

+

+import java.util.LinkedHashSet;

+import java.util.Set;

+

+public class Human extends AbstractHuman<Set<Human>, Human>{

+    public Human() {

+        children = new LinkedHashSet<Human>();

+    }

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/Human2.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human2.java
new file mode 100644
index 0000000..4c8b38f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human2.java
@@ -0,0 +1,14 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive.generics;

+

+import java.util.HashMap;

+import java.util.Map;

+

+public class Human2 extends AbstractHuman<Map<Human2, String>, Human2>{

+

+    public Human2() {

+        children = new HashMap<Human2, String>();

+    }

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/Human3.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human3.java
new file mode 100644
index 0000000..a5bf905
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/Human3.java
@@ -0,0 +1,14 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive.generics;

+

+import java.util.LinkedList;

+import java.util.List;

+

+public class Human3 extends AbstractHuman<List<Human3>, Human3>{

+

+    public Human3() {

+        children = new LinkedList<Human3>();

+    }

+}

diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java
new file mode 100644
index 0000000..91b6452
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java
@@ -0,0 +1,426 @@
+/*

+ * See LICENSE file in distribution for copyright and licensing information.

+ */

+package org.yaml.snakeyaml.recursive.generics;

+

+import java.io.IOException;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.LinkedHashMap;

+import java.util.LinkedHashSet;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.Map.Entry;

+

+import junit.framework.TestCase;

+

+import org.yaml.snakeyaml.Loader;

+import org.yaml.snakeyaml.TypeDescription;

+import org.yaml.snakeyaml.Util;

+import org.yaml.snakeyaml.Yaml;

+import org.yaml.snakeyaml.constructor.Constructor;

+

+public class HumanGenericsTest extends TestCase {

+

+    public void testNoChildren() throws IOException {

+        Human father = new Human();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        Human mother = new Human();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        Yaml yaml = new Yaml();

+        String output = yaml.dump(father);

+        String etalon = Util.getLocalResource("recursive/generics/no-children-1.yaml");

+        assertEquals(etalon, output);

+        //

+        Human father2 = (Human) yaml.load(output);

+        assertNotNull(father2);

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", father2.getParner().getName());

+        assertEquals("Father", father2.getBankAccountOwner().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+    }

+

+    public void testChildren() throws IOException {

+        Human father = new Human();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human mother = new Human();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human son = new Human();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human daughter = new Human();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        Set<Human> children = new LinkedHashSet<Human>(2);

+        children.add(son);

+        children.add(daughter);

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription humanDescription = new TypeDescription(Human.class);

+        humanDescription.putMapPropertyType("children", Human.class, Object.class);

+        constructor.addTypeDescription(humanDescription);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(son);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/generics/with-children.yaml");

+        assertEquals(etalon, output);

+        //

+        Human son2 = (Human) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        Set<Human> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human.class, child.getClass()); // check if type

+                                                       // descriptor was correct

+        }

+    }

+

+    public void testChildren2() throws IOException {

+        Human2 father = new Human2();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human2 mother = new Human2();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human2 son = new Human2();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human2 daughter = new Human2();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        HashMap<Human2, String> children = new LinkedHashMap<Human2, String>(2);

+        children.put(son, "son");

+        children.put(daughter, "daughter");

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription humanDescription = new TypeDescription(Human2.class);

+        humanDescription.putMapPropertyType("children", Human2.class, String.class);

+        constructor.addTypeDescription(humanDescription);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(son);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/generics/with-children-2.yaml");

+        assertEquals(etalon, output);

+        //

+        Human2 son2 = (Human2) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human2 father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        Map<Human2, String> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+    }

+

+    public void testChildren3() throws IOException {

+        Human3 father = new Human3();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human3 mother = new Human3();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human3 son = new Human3();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human3 daughter = new Human3();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        LinkedList<Human3> children = new LinkedList<Human3>();

+        children.add(son);

+        children.add(daughter);

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human3Description = new TypeDescription(Human3.class);

+        Human3Description.putListPropertyType("children", Human3.class);

+        constructor.addTypeDescription(Human3Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(son);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/generics/with-children-3.yaml");

+        assertEquals(etalon, output);

+        //

+        Human3 son2 = (Human3) yaml.load(output);

+        assertNotNull(son2);

+        assertEquals("Son", son.getName());

+

+        Human3 father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        List<Human3> children2 = father2.getChildren();

+        assertEquals(2, children2.size());

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human3.class, child.getClass()); // check if type

+                                                        // descriptor was

+                                                        // correct

+        }

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is set

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenSetAsRoot() throws IOException {

+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-set.yaml");

+

+        Constructor constructor = new Constructor();

+        TypeDescription humanDescription = new TypeDescription(Human.class);

+        humanDescription.putMapPropertyType("children", Human.class, Object.class);

+        constructor.addTypeDescription(humanDescription);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        Set<Human> children2 = (Set<Human>) yaml.load(etalon);

+        assertNotNull(children2);

+        assertEquals(2, children2.size());

+

+        Human firstChild = children2.iterator().next();

+

+        Human father2 = firstChild.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", firstChild.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), firstChild.getMother());

+        assertSame(father2, firstChild.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human.class, child.getClass()); // check if type

+                                                       // descriptor was correct

+        }

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is map

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenMapAsRoot() throws IOException {

+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-map.yaml");

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human2Description = new TypeDescription(Human2.class);

+        Human2Description.putMapPropertyType("children", Human2.class, String.class);

+        constructor.addTypeDescription(Human2Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        Map<Human2, String> children2 = (Map<Human2, String>) yaml.load(etalon);

+        assertNotNull(children2);

+        assertEquals(2, children2.size());

+

+        Entry<Human2, String> firstEntry = children2.entrySet().iterator().next();

+        Human2 firstChild = firstEntry.getKey();

+

+        Human2 father2 = firstChild.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", firstChild.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), firstChild.getMother());

+        assertSame(father2, firstChild.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+    }

+

+    /*

+     * Loads same structure as created in testChildren. But root object is list

+     * of children

+     */

+    @SuppressWarnings("unchecked")

+    public void testChildrenListRoot() throws IOException {

+        Human3 father = new Human3();

+        father.setName("Father");

+        father.setBirthday(new Date(1000000000));

+        father.setBirthPlace("Leningrad");

+        father.setBankAccountOwner(father);

+        //

+        Human3 mother = new Human3();

+        mother.setName("Mother");

+        mother.setBirthday(new Date(100000000000L));

+        mother.setBirthPlace("Saint-Petersburg");

+        father.setParner(mother);

+        mother.setParner(father);

+        mother.setBankAccountOwner(father);

+        //

+        Human3 son = new Human3();

+        son.setName("Son");

+        son.setBirthday(new Date(310000000000L));

+        son.setBirthPlace("Munich");

+        son.setBankAccountOwner(father);

+        son.setFather(father);

+        son.setMother(mother);

+        //

+        Human3 daughter = new Human3();

+        daughter.setName("Daughter");

+        daughter.setBirthday(new Date(420000000000L));

+        daughter.setBirthPlace("New York");

+        daughter.setBankAccountOwner(father);

+        daughter.setFather(father);

+        daughter.setMother(mother);

+        //

+        LinkedList<Human3> children = new LinkedList<Human3>();

+        children.add(son);

+        children.add(daughter);

+        father.setChildren(children);

+        mother.setChildren(children);

+        //

+

+        Constructor constructor = new Constructor();

+        TypeDescription Human3Description = new TypeDescription(Human3.class);

+        Human3Description.putListPropertyType("children", Human3.class);

+        constructor.addTypeDescription(Human3Description);

+

+        Yaml yaml = new Yaml(new Loader(constructor));

+        String output = yaml.dump(father.getChildren());

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-list.yaml");

+        assertEquals(etalon, output);

+        //

+        List<Human3> children2 = (List<Human3>) yaml.load(output);

+        assertNotNull(children2);

+        Human3 son2 = children2.iterator().next();

+        assertEquals(2, children2.size());

+

+        Human3 father2 = son2.getFather();

+        assertEquals("Father", father2.getName());

+        assertEquals("Mother", son2.getMother().getName());

+        assertSame(father2, father2.getBankAccountOwner());

+        assertSame(father2.getParner(), son2.getMother());

+        assertSame(father2, son2.getMother().getParner());

+

+        assertSame(father2.getParner().getChildren(), children2);

+

+        for (Object child : children2) {

+            assertSame(Human3.class, child.getClass()); // check if type

+                                                        // descriptor was

+                                                        // correct

+        }

+    }

+

+    public void testBeanRing() throws IOException {

+        Human man1 = new Human();

+        man1.setName("Man 1");

+        Human man2 = new Human();

+        man2.setName("Man 2");

+        Human man3 = new Human();

+        man3.setName("Man 3");

+        man1.setBankAccountOwner(man2);

+        man2.setBankAccountOwner(man3);

+        man3.setBankAccountOwner(man1);

+        //

+        Yaml yaml = new Yaml();

+        String output = yaml.dump(man1);

+        // System.out.println(output);

+        String etalon = Util.getLocalResource("recursive/generics/beanring-3.yaml");

+        assertEquals(etalon, output);

+        //

+        Human loadedMan1 = (Human) yaml.load(output);

+        assertNotNull(loadedMan1);

+        assertEquals("Man 1", loadedMan1.getName());

+        Human loadedMan2 = loadedMan1.getBankAccountOwner();

+        Human loadedMan3 = loadedMan2.getBankAccountOwner();

+        assertSame(loadedMan1, loadedMan3.getBankAccountOwner());

+    }

+

+}

diff --git a/src/test/resources/recursive/beanring-3.yaml b/src/test/resources/recursive/beanring-3.yaml
index 01167ee..52c52b3 100644
--- a/src/test/resources/recursive/beanring-3.yaml
+++ b/src/test/resources/recursive/beanring-3.yaml
@@ -4,21 +4,21 @@
     bankAccountOwner: *id001

     birthPlace: null

     birthday: null

-    children: []

+    children: !!java.util.LinkedHashSet {}

     father: null

     mother: null

     name: Man 3

     parner: null

   birthPlace: null

   birthday: null

-  children: []

+  children: !!java.util.LinkedHashSet {}

   father: null

   mother: null

   name: Man 2

   parner: null

 birthPlace: null

 birthday: null

-children: []

+children: !!java.util.LinkedHashSet {}

 father: null

 mother: null

 name: Man 1

diff --git a/src/test/resources/recursive/generics/beanring-3.yaml b/src/test/resources/recursive/generics/beanring-3.yaml
new file mode 100644
index 0000000..a4df683
--- /dev/null
+++ b/src/test/resources/recursive/generics/beanring-3.yaml
@@ -0,0 +1,25 @@
+&id001 !!org.yaml.snakeyaml.recursive.generics.Human

+bankAccountOwner: !!org.yaml.snakeyaml.recursive.generics.Human

+  bankAccountOwner: !!org.yaml.snakeyaml.recursive.generics.Human

+    bankAccountOwner: *id001

+    birthPlace: null

+    birthday: null

+    children: !!java.util.LinkedHashSet {}

+    father: null

+    mother: null

+    name: Man 3

+    parner: null

+  birthPlace: null

+  birthday: null

+  children: !!java.util.LinkedHashSet {}

+  father: null

+  mother: null

+  name: Man 2

+  parner: null

+birthPlace: null

+birthday: null

+children: !!java.util.LinkedHashSet {}

+father: null

+mother: null

+name: Man 1

+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/no-children-1.yaml b/src/test/resources/recursive/generics/no-children-1.yaml
new file mode 100644
index 0000000..cb7c4d9
--- /dev/null
+++ b/src/test/resources/recursive/generics/no-children-1.yaml
@@ -0,0 +1,17 @@
+&id001 !!org.yaml.snakeyaml.recursive.generics.Human

+bankAccountOwner: *id001

+birthPlace: Leningrad

+birthday: 1970-01-12T13:46:40Z

+children: !!java.util.LinkedHashSet {}

+father: null

+mother: null

+name: Father

+parner: !!org.yaml.snakeyaml.recursive.generics.Human

+  bankAccountOwner: *id001

+  birthPlace: Saint-Petersburg

+  birthday: 1973-03-03T09:46:40Z

+  children: !!java.util.LinkedHashSet {}

+  father: null

+  mother: null

+  name: Mother

+  parner: *id001
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-2.yaml b/src/test/resources/recursive/generics/with-children-2.yaml
new file mode 100644
index 0000000..7460bbd
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-2.yaml
@@ -0,0 +1,35 @@
+&id002 !!org.yaml.snakeyaml.recursive.generics.Human2
+bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human2
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+    *id002: son
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: {}
+      father: *id001
+      mother: &id004 !!org.yaml.snakeyaml.recursive.generics.Human2
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        parner: *id001
+      name: Daughter
+      parner: null
+    : daughter
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: {}
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-3.yaml b/src/test/resources/recursive/generics/with-children-3.yaml
new file mode 100644
index 0000000..f81f2ee
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-3.yaml
@@ -0,0 +1,34 @@
+&id002 !!org.yaml.snakeyaml.recursive.generics.Human3
+bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human3
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004 !!org.yaml.snakeyaml.recursive.generics.Human3
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+    name: Daughter
+    parner: null
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-as-list.yaml b/src/test/resources/recursive/generics/with-children-as-list.yaml
new file mode 100644
index 0000000..eb04f93
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-list.yaml
@@ -0,0 +1,35 @@
+&id002
+- !!org.yaml.snakeyaml.recursive.generics.Human3
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human3
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.generics.Human3
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+- !!org.yaml.snakeyaml.recursive.generics.Human3
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-as-map.yaml b/src/test/resources/recursive/generics/with-children-as-map.yaml
new file mode 100644
index 0000000..501c843
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-map.yaml
@@ -0,0 +1,37 @@
+&id002 !!java.util.LinkedHashMap
+? !!org.yaml.snakeyaml.recursive.generics.Human2
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human2
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.generics.Human2
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+: This is My Son
+? !!org.yaml.snakeyaml.recursive.generics.Human2
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
+: This Is My Daughter
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-as-set.yaml b/src/test/resources/recursive/generics/with-children-as-set.yaml
new file mode 100644
index 0000000..2299b3b
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-set.yaml
@@ -0,0 +1,37 @@
+&id002 !!set
+? !!org.yaml.snakeyaml.recursive.generics.Human
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.generics.Human
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+: null
+? !!org.yaml.snakeyaml.recursive.generics.Human
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
+: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children.yaml b/src/test/resources/recursive/generics/with-children.yaml
new file mode 100644
index 0000000..0e9bf48
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children.yaml
@@ -0,0 +1,35 @@
+&id002 !!org.yaml.snakeyaml.recursive.generics.Human
+bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.Human
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003 !!java.util.LinkedHashSet
+    *id002: null
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: !!java.util.LinkedHashSet {}
+      father: *id001
+      mother: &id004 !!org.yaml.snakeyaml.recursive.generics.Human
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        parner: *id001
+      name: Daughter
+      parner: null
+    : null
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: !!java.util.LinkedHashSet {}
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/no-children-1.yaml b/src/test/resources/recursive/no-children-1.yaml
index d86035c..449473c 100644
--- a/src/test/resources/recursive/no-children-1.yaml
+++ b/src/test/resources/recursive/no-children-1.yaml
@@ -2,7 +2,7 @@
 bankAccountOwner: *id001

 birthPlace: Leningrad

 birthday: 1970-01-12T13:46:40Z

-children: []

+children: !!java.util.LinkedHashSet {}

 father: null

 mother: null

 name: Father

@@ -10,7 +10,7 @@
   bankAccountOwner: *id001

   birthPlace: Saint-Petersburg

   birthday: 1973-03-03T09:46:40Z

-  children: []

+  children: !!java.util.LinkedHashSet {}

   father: null

   mother: null

   name: Mother

diff --git a/src/test/resources/recursive/with-children-2.yaml b/src/test/resources/recursive/with-children-2.yaml
index aad8af8..4ed2047 100644
--- a/src/test/resources/recursive/with-children-2.yaml
+++ b/src/test/resources/recursive/with-children-2.yaml
@@ -1,33 +1,35 @@
-&id001 !!org.yaml.snakeyaml.recursive.Human

-bankAccountOwner: *id001

-birthPlace: Leningrad

-birthday: 1970-01-12T13:46:40Z

-children: &id002

-- bankAccountOwner: *id001

-  birthPlace: Munich

-  birthday: 1979-10-28T23:06:40Z

-  children: []

-  father: *id001

-  mother: &id003

-    bankAccountOwner: *id001

-    birthPlace: Saint-Petersburg

-    birthday: 1973-03-03T09:46:40Z

-    children: *id002

-    father: null

-    mother: null

-    name: Mother

-    parner: *id001

-  name: Son

-  parner: null

-- bankAccountOwner: *id001

-  birthPlace: New York

-  birthday: 1983-04-24T02:40:00Z

-  children: []

-  father: *id001

-  mother: *id003

-  name: Daughter

-  parner: null

-father: null

-mother: null

-name: Father

-parner: *id003

+&id002 !!org.yaml.snakeyaml.recursive.Human2
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+    *id002: son
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        parner: *id001
+      name: Daughter
+      parner: null
+    : daughter
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: {}
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-3.yaml b/src/test/resources/recursive/with-children-3.yaml
new file mode 100644
index 0000000..abca3b5
--- /dev/null
+++ b/src/test/resources/recursive/with-children-3.yaml
@@ -0,0 +1,34 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human3
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+    name: Daughter
+    parner: null
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-as-list.yaml b/src/test/resources/recursive/with-children-as-list.yaml
new file mode 100644
index 0000000..faf28cd
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-list.yaml
@@ -0,0 +1,35 @@
+&id002
+- !!org.yaml.snakeyaml.recursive.Human3
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.Human3
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.Human3
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+- !!org.yaml.snakeyaml.recursive.Human3
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-as-map.yaml b/src/test/resources/recursive/with-children-as-map.yaml
new file mode 100644
index 0000000..ebce1a4
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-map.yaml
@@ -0,0 +1,37 @@
+&id002 !!java.util.LinkedHashMap
+? !!org.yaml.snakeyaml.recursive.Human2
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.Human2
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.Human2
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+: This is My Son
+? !!org.yaml.snakeyaml.recursive.Human2
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
+: This Is My Daughter
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-as-set.yaml b/src/test/resources/recursive/with-children-as-set.yaml
new file mode 100644
index 0000000..d3ef5ed
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-set.yaml
@@ -0,0 +1,37 @@
+&id002 !!set
+? !!org.yaml.snakeyaml.recursive.Human
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.Human
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    parner: &id003 !!org.yaml.snakeyaml.recursive.Human
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      parner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Son
+  parner: null
+: null
+? !!org.yaml.snakeyaml.recursive.Human
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  parner: null
+: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children.yaml b/src/test/resources/recursive/with-children.yaml
new file mode 100644
index 0000000..0d98bb9
--- /dev/null
+++ b/src/test/resources/recursive/with-children.yaml
@@ -0,0 +1,35 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003 !!java.util.LinkedHashSet
+    *id002: null
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: !!java.util.LinkedHashSet {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        parner: *id001
+      name: Daughter
+      parner: null
+    : null
+  father: null
+  mother: null
+  name: Father
+  parner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: !!java.util.LinkedHashSet {}
+father: *id001
+mother: *id004
+name: Son
+parner: null
\ No newline at end of file