Finish issue73; do not omit !!set tag for JavaBean properties
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e36ac20..c6441f1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,9 @@
 	</properties>

 	<body>

           <release version="1.7" date="in Mercurial" description="development">

+            <action dev="py4fun" type="add" issue="73" due-to="birnbuazn">

+                Provide sequence support for loading java.util.Set (2010-07-19)

+            </action>

             <action dev="py4fun" type="add" issue="72" due-to="birnbuazn">

                 Support java.util.Collection as a parent for List and Set (2010-07-09)

             </action>

diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
index a2bbc77..6e01224 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -252,8 +252,8 @@
     @SuppressWarnings("unchecked")
     protected Set<? extends Object> constructSet(SequenceNode node) {
         Set<Object> result;
-        if (Set.class.isAssignableFrom(node.getType()) && !node.getType().isInterface()) {
-            // the root class may be defined (Vector for instance)
+        if (!node.getType().isInterface()) {
+            // the root class may be defined
             try {
                 result = (Set<Object>) node.getType().newInstance();
             } catch (Exception e) {
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index 40787e6..755245c 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -161,9 +161,8 @@
                 }
             } else if (SortedSet.class.isAssignableFrom(node.getType())) {
                 SortedSet<Object> set = new TreeSet<Object>();
-                if (!node.isTwoStepsConstruction()) {
-                    constructSet2ndStep(mnode, set);
-                }
+                // set cannot be recursive
+                constructSet2ndStep(mnode, set);
                 return set;
             } else if (Collection.class.isAssignableFrom(node.getType())) {
                 if (node.isTwoStepsConstruction()) {
@@ -491,7 +490,7 @@
             SequenceNode snode = (SequenceNode) node;
             if (Set.class.isAssignableFrom(node.getType())) {
                 if (node.isTwoStepsConstruction()) {
-                    return createDefaultSet(snode.getValue().size());
+                    throw new YAMLException("Set cannot be recursive.");
                 } else {
                     return constructSet(snode);
                 }
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
index 6c0d147..89ec57d 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
@@ -126,8 +126,8 @@
             // the node is a map, set or JavaBean
             if (!Map.class.isAssignableFrom(propertyValue.getClass())) {
                 // the node is set or JavaBean
-                if (customTag == null) {
-                    // custom tag is not defined
+                if (customTag == null && !nodeValue.getTag().equals(Tag.SET)) {
+                    // custom tag is not defined, set must keep the '!!set' tag
                     if (property.getType() == propertyValue.getClass()) {
                         // we do not need global tag because the property
                         // Class is the same as the runtime class
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java
new file mode 100644
index 0000000..34ed71e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2008-2010, http://code.google.com/p/snakeyaml/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test bean when the implementation is defined: ArrayList instead of just the
+ * interface List
+ */
+public class ArrayListTest extends TestCase {
+    public void testListImplementation() {
+        Bean1 bean = new Bean1();
+        bean.setId("ID123");
+        ArrayList<String> list = new ArrayList<String>(3);
+        list.add("zzz");
+        list.add("xxx");
+        list.add("ccc");
+        bean.setList(list);
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(bean);
+        // System.out.println(doc);
+        Bean1 loaded = (Bean1) yaml.load(doc);
+        assertEquals(3, loaded.getList().size());
+        assertEquals(ArrayList.class, loaded.getList().getClass());
+    }
+
+    public static class Bean1 {
+        private ArrayList<String> list;
+        private String id;
+
+        public ArrayList<String> getList() {
+            return list;
+        }
+
+        public void setList(ArrayList<String> list) {
+            this.list = list;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java
index 2ae3ee6..55474ac 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java
@@ -25,6 +25,7 @@
     private String name;
     private Set<Post> posts = new TreeSet<Post>();
     public Set<Integer> numbers = new LinkedHashSet<Integer>();
+    private TreeSet<String> labels = new TreeSet<String>();
 
     public Blog() {
         name = "SuperBlog";
@@ -54,6 +55,14 @@
         this.posts = posts;
     }
 
+    public TreeSet<String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(TreeSet<String> labels) {
+        this.labels = labels;
+    }
+
     @Override
     public boolean equals(Object obj) {
         return name.equals(obj.toString());
@@ -68,5 +77,4 @@
     public String toString() {
         return "Blog '" + name + "'";
     }
-
 }
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java
index 85f85e7..259c528 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java
@@ -41,4 +41,24 @@
     public int compareTo(Post o) {
         return title.compareTo(o.title);
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Post) {
+            return toString().equals(obj.toString());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Post " + title + " " + text;
+    }
+
 }
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java
new file mode 100644
index 0000000..1a35727
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2008-2010, http://code.google.com/p/snakeyaml/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class RecursiveSetTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testDumpException() {
+        Set set1 = new HashSet();
+        Set set2 = new HashSet();
+        set1.add(set2);
+        set2.add(set1);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(set1);
+        } catch (StackOverflowError e) {
+            assertEquals(null, e.getMessage());
+        }
+    }
+
+    public void testLoadException() {
+        String doc = Util.getLocalResource("issues/issue73-recursive4.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(doc);
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Set cannot be recursive."));
+        }
+    }
+
+    public void testLoadException2() {
+        String doc = Util.getLocalResource("issues/issue73-recursive5.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(doc);
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Set cannot be recursive."));
+        }
+    }
+
+    public static class Bean1 {
+        private Set<Object> set;
+        private String id;
+
+        public Set<Object> getSet() {
+            return set;
+        }
+
+        public void setSet(Set<Object> set) {
+            this.set = set;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java
index de2ed00..9c8d720 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java
@@ -17,6 +17,7 @@
 package org.yaml.snakeyaml.issues.issue73;
 
 import java.util.Set;
+import java.util.TreeSet;
 
 import junit.framework.TestCase;
 
@@ -36,6 +37,11 @@
         blog.addPost(new Post("Title2", "text text 2"));
         blog.numbers.add(19);
         blog.numbers.add(17);
+        TreeSet<String> labels = new TreeSet<String>();
+        labels.add("Java");
+        labels.add("YAML");
+        labels.add("SnakeYAML");
+        blog.setLabels(labels);
         DumperOptions options = new DumperOptions();
         options.setAllowReadOnlyProperties(true);
         Yaml yaml = new Yaml(new Dumper(options));
@@ -71,10 +77,22 @@
     protected void checkTestBlog(Blog blog) {
         Set<Post> posts = blog.getPosts();
         assertEquals("Blog contains 2 posts", 2, posts.size());
-        for (Post post : posts) {
-            assertEquals(Post.class, post.getClass());
-        }
+        assertTrue(posts.contains(new Post("Test", "Dummy")));
+        assertTrue(posts.contains(new Post("Highly", "Creative")));
         assertEquals("No tags!", blog.getName());
         assertEquals(0, blog.numbers.size());
+
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadRootSet() {
+        Yaml yaml = new Yaml();
+        String doc = Util.getLocalResource("issues/issue73-3.txt");
+        Set<String> strings = (Set<String>) yaml.load(doc);
+        // System.out.println(strings);
+        assertEquals(3, strings.size());
+        assertTrue(strings.contains("aaa"));
+        assertTrue(strings.contains("bbb"));
+        assertTrue(strings.contains("ccc"));
     }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java
new file mode 100644
index 0000000..308e730
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008-2010, http://code.google.com/p/snakeyaml/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test bean when the implementation is defined: TreeSet instead of just the
+ * interface Set
+ */
+public class TreeSetTest extends TestCase {
+    public void testSetImplementation() {
+        Bean1 bean = new Bean1();
+        bean.setId("ID123");
+        TreeSet<String> list = new TreeSet<String>();
+        list.add("zzz");
+        list.add("xxx");
+        list.add("ccc");
+        bean.setSet(list);
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(bean);
+        // System.out.println(doc);
+        //
+        Bean1 loaded = (Bean1) yaml.load(doc);
+        assertEquals(3, loaded.getSet().size());
+        assertEquals(TreeSet.class, loaded.getSet().getClass());
+        assertTrue(loaded.getSet().contains("zzz"));
+        assertTrue(loaded.getSet().contains("xxx"));
+        assertTrue(loaded.getSet().contains("ccc"));
+    }
+
+    public static class Bean1 {
+        private TreeSet<String> set;
+        private String id;
+
+        public TreeSet<String> getSet() {
+            return set;
+        }
+
+        public void setSet(TreeSet<String> set) {
+            this.set = set;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/resources/issues/issue73-1.txt b/src/test/resources/issues/issue73-1.txt
index 67be3af..78f262a 100644
--- a/src/test/resources/issues/issue73-1.txt
+++ b/src/test/resources/issues/issue73-1.txt
@@ -1,4 +1,5 @@
 !!org.yaml.snakeyaml.issues.issue73.Blog
+labels: !!set {Java: null, SnakeYAML: null, YAML: null}
 name: Test Me!
 numbers: !!set {19: null, 17: null}
 posts: !!set
diff --git a/src/test/resources/issues/issue73-3.txt b/src/test/resources/issues/issue73-3.txt
new file mode 100644
index 0000000..c188fe0
--- /dev/null
+++ b/src/test/resources/issues/issue73-3.txt
@@ -0,0 +1,5 @@
+!!java.util.HashSet
+- aaa
+- bbb
+- ccc
+
diff --git a/src/test/resources/issues/issue73-recursive4.txt b/src/test/resources/issues/issue73-recursive4.txt
new file mode 100644
index 0000000..5141208
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive4.txt
@@ -0,0 +1,5 @@
+&id001 !!java.util.HashSet
+- aaa
+- !!set { 1, *id001 }
+- ccc
+
diff --git a/src/test/resources/issues/issue73-recursive5.txt b/src/test/resources/issues/issue73-recursive5.txt
new file mode 100644
index 0000000..ddb7c42
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive5.txt
@@ -0,0 +1,4 @@
+!!org.yaml.snakeyaml.issues.issue73.RecursiveSetTest$Bean1
+id: ID123
+set: &id001 !!set {ccc: null, *id001: null, zzz: null}
+