Deliver possibility to load immutable instances with no global tags
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4375332..4178922 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,14 +7,17 @@
 	</properties>

 	<body>

 	     <release version="1.4-SNAPSHOT" date="in Mercurial" description="development">

-	        <action dev="py4fun" type="update">

+	        <action dev="py4fun" type="add">

+                Deliver possibility to load immutable instances with no global tags. Reflection for

+                constructor arguments is used to get the runtime classes (2009-08-04)

+            </action>

+            <action dev="py4fun" type="update">

                 Use more informative error message when a JavaBean property cannot

-                be created (2009-08-08)

+                be created (2009-08-02)

             </action>

             <action dev="py4fun" type="update">

                 Refactor: Constructor is rewritten. Do not overwrite methods from BaseConstructor. 

-                Instead introduce ConstructScalar, ConstructSequence, ConstructMapping.

-                Tests are not finished yet (2009-07-31)

+                Instead introduce ConstructScalar, ConstructSequence, ConstructMapping (2009-07-31)

             </action>

             <action dev="py4fun" type="update">

                 Change Maven repository path: groupId='org.yaml', artifactId='snakeyaml' (2009-07-31)

diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index e621c32..e805199 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -13,6 +13,7 @@
 import java.math.BigInteger;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -429,7 +430,34 @@
                 }
             } else {
                 // create immutable object
-                List<Object> argumentList = (List<Object>) constructSequence(snode);
+                List<java.lang.reflect.Constructor> possibleConstructors = new LinkedList<java.lang.reflect.Constructor>();
+                for (java.lang.reflect.Constructor constructor : node.getType().getConstructors()) {
+                    if (snode.getValue().size() == constructor.getParameterTypes().length) {
+                        possibleConstructors.add(constructor);
+                    }
+                }
+                if (possibleConstructors.isEmpty()) {
+                    throw new YAMLException("No constructors with "
+                            + String.valueOf(snode.getValue().size()) + " arguments found for "
+                            + node.getType());
+                }
+                List<Object> argumentList;
+                if (possibleConstructors.size() == 1) {
+                    argumentList = new LinkedList<Object>();
+                    java.lang.reflect.Constructor c = possibleConstructors.get(0);
+                    int index = 0;
+                    for (Node argumentNode : snode.getValue()) {
+                        Class type = c.getParameterTypes()[index];
+                        // set runtime classes for arguments
+                        argumentNode.setType(type);
+                        Object argumentValue = constructObject(argumentNode);
+                        argumentList.add(argumentValue);
+                        index++;
+                    }
+                } else {
+                    // use BaseConstructor
+                    argumentList = (List<Object>) constructSequence(snode);
+                }
                 Class[] parameterTypes = new Class[argumentList.size()];
                 int index = 0;
                 for (Object parameter : argumentList) {
@@ -455,7 +483,7 @@
             if (List.class.isAssignableFrom(node.getType())) {
                 constructSequenceStep2(snode, list);
             } else {
-                throw new UnsupportedOperationException("Immutable objects cannot be recursive.");
+                throw new YAMLException("Immutable objects cannot be recursive.");
             }
         }
     }
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
index 80d469c..30948b4 100644
--- a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
@@ -98,7 +98,7 @@
         javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
 
         String dumpStr = yaml.dump(javaBeanWithNullValues);
-        System.out.println(dumpStr);
+        // System.out.println(dumpStr);
         assertFalse("No explicit root tag must be used.", dumpStr
                 .contains("JavaBeanWithNullValues"));
         yaml = new Yaml();
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java
index e3e34cf..3648fd8 100644
--- a/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java
@@ -70,7 +70,6 @@
         Loader loader = new Loader(constructor);

         Yaml yaml = new Yaml(loader);

         String source = Util.getLocalResource("constructor/car-without-tags.yaml");

-        System.out.println(source);

         Car car = (Car) yaml.load(source);

         assertEquals("12-XP-F4", car.getPlate());

         List<Wheel> wheels = car.getWheels();

diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code.java b/src/test/java/org/yaml/snakeyaml/immutable/Code.java
index d176abd..bc2ebe3 100644
--- a/src/test/java/org/yaml/snakeyaml/immutable/Code.java
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code.java
@@ -31,6 +31,6 @@
 
     @Override
     public String toString() {
-        return "Code code=" + code;
+        return "<Code code=" + code + ">";
     }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code2.java b/src/test/java/org/yaml/snakeyaml/immutable/Code2.java
new file mode 100644
index 0000000..0d1e97d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code2.java
@@ -0,0 +1,43 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * Two constructors with 1 argument. These immutable objects are not supported.
+ */
+public class Code2 {
+    private final Integer code;
+
+    public Code2(Integer name) {
+        this.code = name;
+    }
+
+    public Code2(String name) {
+        this.code = Integer.parseInt(name);
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code2) {
+            Code2 code = (Code2) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code2 code=" + code + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code3.java b/src/test/java/org/yaml/snakeyaml/immutable/Code3.java
new file mode 100644
index 0000000..e4ebf92
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code3.java
@@ -0,0 +1,41 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * No constructors with 1 argument. These immutable objects are not supported.
+ */
+public class Code3 {
+    private final String name;
+    private final Integer code;
+
+    public Code3(String name, Integer code) {
+        this.code = code;
+        this.name = name;
+    }
+
+    public String getData() {
+        return name + code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code3) {
+            Code3 code = (Code3) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code3 data=" + getData() + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Color.java b/src/test/java/org/yaml/snakeyaml/immutable/Color.java
index e5a8762..0271f5d 100644
--- a/src/test/java/org/yaml/snakeyaml/immutable/Color.java
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Color.java
@@ -31,6 +31,6 @@
 
     @Override
     public String toString() {
-        return "Color id=" + name;
+        return "<Color id=" + name + ">";
     }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point.java b/src/test/java/org/yaml/snakeyaml/immutable/Point.java
index 4fbe9c2..7ceb888 100644
--- a/src/test/java/org/yaml/snakeyaml/immutable/Point.java
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point.java
@@ -20,4 +20,9 @@
         this.x = x;
         this.y = y;
     }
+
+    @Override
+    public String toString() {
+        return "<Point x=" + String.valueOf(x) + " y=" + String.valueOf(y) + ">";
+    }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point2.java b/src/test/java/org/yaml/snakeyaml/immutable/Point2.java
new file mode 100644
index 0000000..f85efad
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point2.java
@@ -0,0 +1,35 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * Two public constructor with 2 argument are present
+ */
+public class Point2 {
+    private final Integer x;
+    private final Integer y;
+
+    public Integer getX() {
+        return x;
+    }
+
+    public Integer getY() {
+        return y;
+    }
+
+    public Point2(Double x, Double y) {
+        this.x = x.intValue();
+        this.y = y.intValue();
+    }
+
+    public Point2(Integer x, Integer y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    @Override
+    public String toString() {
+        return "<Point2 x=" + String.valueOf(x) + " y=" + String.valueOf(y) + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java b/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java
index f1f9872..f98a033 100644
--- a/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java
@@ -19,4 +19,9 @@
     public Point getPoint() {
         return point;
     }
+
+    @Override
+    public String toString() {
+        return "<Point3d point=" + point.toString() + " z=" + String.valueOf(z) + ">";
+    }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/ShapeCustomDumperTest.java b/src/test/java/org/yaml/snakeyaml/immutable/ShapeCustomDumperTest.java
deleted file mode 100644
index 0b535a8..0000000
--- a/src/test/java/org/yaml/snakeyaml/immutable/ShapeCustomDumperTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.yaml.snakeyaml.immutable;
-
-import junit.framework.TestCase;
-
-import org.yaml.snakeyaml.Util;
-import org.yaml.snakeyaml.Yaml;
-
-public class ShapeCustomDumperTest extends TestCase {
-
-    public void testColor() {
-        Yaml yaml = new Yaml();
-        Color loaded = (Color) yaml.load("!!org.yaml.snakeyaml.immutable.Color BLACK");
-        assertEquals("BLACK", loaded.getName());
-    }
-
-    public void testCode() {
-        Yaml yaml = new Yaml();
-        Code loaded = (Code) yaml.load("!!org.yaml.snakeyaml.immutable.Code 123");
-        assertEquals(new Integer(123), loaded.getCode());
-    }
-
-    public void testSuperColorFail() {
-        Yaml yaml = new Yaml();
-        try {
-            yaml.load("!!org.yaml.snakeyaml.immutable.SuperColor BLACK");
-            fail("SuperColor requires Color and not a String.");
-        } catch (Exception e) {
-            assertEquals(
-                    "null; Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.SuperColor; exception=Unsupported class: class org.yaml.snakeyaml.immutable.Color",
-                    e.getMessage());
-        }
-    }
-
-    public void testPoint() {
-        Yaml yaml = new Yaml();
-        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14]");
-        assertEquals(1.17, loaded.getX());
-        assertEquals(3.14, loaded.getY());
-    }
-
-    public void testPointBlock() {
-        Yaml yaml = new Yaml();
-        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point\n- 1.17\n- 3.14");
-        assertEquals(1.17, loaded.getX());
-        assertEquals(3.14, loaded.getY());
-    }
-
-    public void testPoint3d() {
-        Yaml yaml = new Yaml();
-        Point3d loaded = (Point3d) yaml
-                .load("!!org.yaml.snakeyaml.immutable.Point3d [!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14], 345.1]");
-        assertEquals(345.1, loaded.getZ());
-    }
-
-    public void testShape() {
-        Yaml yaml = new Yaml();
-        String source = Util.getLocalResource("immutable/shape1.yaml");
-        System.out.println(source);
-        Shape loaded = (Shape) yaml.load(source);
-        assertEquals(new Integer(123), loaded.getId());
-    }
-
-}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java b/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java
new file mode 100644
index 0000000..e3c7332
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java
@@ -0,0 +1,129 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml.immutable;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.JavaBeanLoader;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class ShapeImmutableTest extends TestCase {
+
+    public void testColor() {
+        Yaml yaml = new Yaml();
+        Color loaded = (Color) yaml.load("!!org.yaml.snakeyaml.immutable.Color BLACK");
+        assertEquals("BLACK", loaded.getName());
+    }
+
+    public void testCode() {
+        Yaml yaml = new Yaml();
+        Code loaded = (Code) yaml.load("!!org.yaml.snakeyaml.immutable.Code 123");
+        assertEquals(new Integer(123), loaded.getCode());
+    }
+
+    public void testSuperColor() {
+        Yaml yaml = new Yaml();
+        SuperColor superColor = (SuperColor) yaml
+                .load("!!org.yaml.snakeyaml.immutable.SuperColor [!!org.yaml.snakeyaml.immutable.Color BLACK]");
+        assertEquals("BLACK", superColor.getColor().getName());
+    }
+
+    public void testSuperColorFail() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.SuperColor BLACK");
+            fail("SuperColor requires Color and not a String.");
+        } catch (Exception e) {
+            assertEquals(
+                    "null; Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.SuperColor; exception=Unsupported class: class org.yaml.snakeyaml.immutable.Color",
+                    e.getMessage());
+        }
+    }
+
+    public void testCode2() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Code2 555");
+            fail("There must be only 1 constructor with 1 argument for scalar.");
+        } catch (Exception e) {
+            assertEquals(
+                    "null; Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Code2; exception=More then 1 constructor with 1 argument found for class org.yaml.snakeyaml.immutable.Code2",
+                    e.getMessage());
+        }
+    }
+
+    public void testCode3() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Code3 777");
+            fail("There must be 1 constructor with 1 argument for scalar.");
+        } catch (Exception e) {
+            assertEquals(
+                    "null; Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Code3; exception=No single argument constructor found for class org.yaml.snakeyaml.immutable.Code3",
+                    e.getMessage());
+        }
+    }
+
+    public void testPoint() {
+        Yaml yaml = new Yaml();
+        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14]");
+        assertEquals(1.17, loaded.getX());
+        assertEquals(3.14, loaded.getY());
+    }
+
+    public void testPointBlock() {
+        Yaml yaml = new Yaml();
+        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point\n- 1.17\n- 3.14");
+        assertEquals(1.17, loaded.getX());
+        assertEquals(3.14, loaded.getY());
+    }
+
+    public void testPointOnlyOneArgument() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Point\n- 1.17");
+            fail("Two arguments required.");
+        } catch (Exception e) {
+            assertEquals(
+                    "null; Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Point; exception=No constructors with 1 arguments found for class org.yaml.snakeyaml.immutable.Point",
+                    e.getMessage());
+        }
+    }
+
+    public void testPoint2() {
+        Yaml yaml = new Yaml();
+        Point2 loaded = (Point2) yaml.load("!!org.yaml.snakeyaml.immutable.Point2\n- 1\n- 3");
+        assertEquals(new Integer(1), loaded.getX());
+        assertEquals(new Integer(3), loaded.getY());
+    }
+
+    public void testPoint3d() {
+        Yaml yaml = new Yaml();
+        Point3d loaded = (Point3d) yaml
+                .load("!!org.yaml.snakeyaml.immutable.Point3d [!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14], 345.1]");
+        assertEquals(345.1, loaded.getZ());
+    }
+
+    public void testShape() {
+        Yaml yaml = new Yaml();
+        String source = Util.getLocalResource("immutable/shape1.yaml");
+        Shape loaded = (Shape) yaml.load(source);
+        assertEquals(new Integer(123), loaded.getId());
+    }
+
+    public void testShapeNoTags() {
+        String source = Util.getLocalResource("immutable/shapeNoTags.yaml");
+        JavaBeanLoader<Shape> beanLoader = new JavaBeanLoader<Shape>(Shape.class);
+        Shape loaded = beanLoader.load(source);
+        assertEquals(new Integer(123), loaded.getId());
+        assertEquals("BLACK", loaded.getColor().getName());
+        assertEquals(1.17, loaded.getPoint().getX());
+        assertEquals(3.14, loaded.getPoint().getY());
+        assertEquals(345.1, loaded.getPoint3d().getZ());
+        assertEquals(1.96, loaded.getPoint3d().getPoint().getX());
+        assertEquals(1.78, loaded.getPoint3d().getPoint().getY());
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java b/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java
index 2acec50..0df6a4b 100644
--- a/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java
+++ b/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java
@@ -33,4 +33,8 @@
     public String toString() {
         return "SuperColor color=" + color;
     }
+
+    public Color getColor() {
+        return color;
+    }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
index 66f616f..e4a7bdf 100644
--- a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
@@ -18,7 +18,6 @@
         bean.setName("Bean25");

         JavaBeanDumper beanDumper = new JavaBeanDumper();

         String output = beanDumper.dump(bean);

-        System.out.println(output);

         assertEquals(

                 "name: Bean25\nshape: !!org.yaml.snakeyaml.javabeans.Triangle\n  name: Triangle25\n",

                 output);

diff --git a/src/test/resources/immutable/shapeNoTags.yaml b/src/test/resources/immutable/shapeNoTags.yaml
new file mode 100644
index 0000000..3ae087a
--- /dev/null
+++ b/src/test/resources/immutable/shapeNoTags.yaml
@@ -0,0 +1,8 @@
+color: BLACK

+point:

+- 1.17

+- 3.14

+point3d:

+ - [1.96, 1.78]

+ - 345.1

+id: 123
\ No newline at end of file