Fix issue11: create a Java instance with the following priority to choose the class: Explicit tag -> Runtime class (defined in JavaBean) -> implicit tag
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 452704b..67ad4cb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -8,9 +8,8 @@
 	<body>

 	     <release version="1.4-SNAPSHOT" date="in Mercurial" description="better support for immutable objects">

 	        <action dev="py4fun" type="fix" issue="11" due-to="infinity0x">

-                Fix: in case there is more then 1 constructor with 1 argument to create an instance

-                for scalar node and a global or local tag does not allow to rely on the implicit type

-                then try to use the constructor with the String argument (2009-08-05)

+                Fix: create a Java instance with the following priority to choose the class:

+                Explicit tag -> Runtime class (defined in JavaBean) -> implicit tag  (2009-08-06)

             </action>

             <action dev="py4fun" type="fix" issue="9" due-to="wwagner4">

                 Fix: Bean with no property cannot be instantiated. This is implemented via better

diff --git a/src/main/java/org/yaml/snakeyaml/composer/Composer.java b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
index a9ad0b4..bbd0ea9 100644
--- a/src/main/java/org/yaml/snakeyaml/composer/Composer.java
+++ b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
@@ -133,11 +133,13 @@
     private Node composeScalarNode(String anchor) {
         ScalarEvent ev = (ScalarEvent) parser.getEvent();
         String tag = ev.getTag();
+        boolean explicitTag = true;
         if (tag == null || tag.equals("!")) {
             tag = resolver.resolve(NodeId.scalar, ev.getValue(), ev.getImplicit()[0]);
+            explicitTag = false;
         }
-        Node node = new ScalarNode(tag, ev.getValue(), ev.getStartMark(), ev.getEndMark(), ev
-                .getStyle());
+        Node node = new ScalarNode(tag, explicitTag, ev.getValue(), ev.getStartMark(), ev
+                .getEndMark(), ev.getStyle());
         if (anchor != null) {
             anchors.put(anchor, node);
         }
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
index 686f269..7d8e672 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -30,13 +30,16 @@
 public abstract class BaseConstructor {
     /**
      * It maps the node kind to the the Construct implementation. When the
-     * runtime class is known then the tag (even explicit) is ignored.
+     * runtime class is known then the implicit tag is ignored.
      */
-    protected final Map<NodeId, Construct> yamlClassConstructors = new EnumMap<NodeId, Construct>(NodeId.class);
+    protected final Map<NodeId, Construct> yamlClassConstructors = new EnumMap<NodeId, Construct>(
+            NodeId.class);
     /**
-     * It maps a the resolved tag to the Construct implementation. It is used
-     * when the runtime class of the instance is unknown (the node has the
-     * Object.class)
+     * It maps the (explicit or implicit) tag to the Construct implementation.
+     * It is used: <br/>
+     * 1) explicit tag - if present. <br/>
+     * 2) implicit tag - when the runtime class of the instance is unknown (the
+     * node has the Object.class)
      */
     protected final Map<String, Construct> yamlConstructors = new HashMap<String, Construct>();
 
@@ -177,16 +180,17 @@
     }
 
     /**
-     * Get the constructor to construct the Node. If the runtime class is known
-     * a dedicated Construct implementation is used. Otherwise the constructor
-     * is chosen by the tag.
+     * Get the constructor to construct the Node. For implicit tags if the
+     * runtime class is known a dedicated Construct implementation is used.
+     * Otherwise the constructor is chosen by the tag.
      * 
      * @param node
      *            Node to be constructed
      * @return Construct implementation for the specified node
      */
     private Construct getConstructor(Node node) {
-        if (!Object.class.equals(node.getType()) && !node.getTag().equals(Tags.NULL)) {
+        if (!node.hasExplicitTag() && !Object.class.equals(node.getType())
+                && !node.getTag().equals(Tags.NULL)) {
             return yamlClassConstructors.get(node.getNodeId());
         } else {
             Construct constructor = yamlConstructors.get(node.getTag());
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index b7d5719..64d30b4 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -305,10 +305,6 @@
                 result = constructStandardJavaInstance(type, node);
             } else {
                 // there must be only 1 constructor with 1 argument
-                if (Modifier.isAbstract(type.getModifiers())) {
-                    // use the tag when the runtime class cannot be instantiated
-                    type = getClassForNode(node);
-                }
                 java.lang.reflect.Constructor[] javaConstructors = type.getConstructors();
                 int oneArgCount = 0;
                 java.lang.reflect.Constructor javaConstructor = null;
@@ -328,7 +324,9 @@
                     // TODO it should be possible to use implicit types instead
                     // of forcing String. Resolver must be available here to
                     // obtain the implicit tag. Then we can set the tag and call
-                    // callConstructor(node) to create the argument instance
+                    // callConstructor(node) to create the argument instance.
+                    // On the other hand it may be safer to require a custom
+                    // constructor to avoid guessing the argument class
                     argument = constructScalar(node);
                     try {
                         javaConstructor = type.getConstructor(String.class);
@@ -417,9 +415,6 @@
                     throw new YAMLException("Unable to find enum value '" + enumValueName
                             + "' for enum class: " + type.getName());
                 }
-            } else if (Tags.BINARY.equals(node.getTag())) {
-                Construct intConstructor = yamlConstructors.get(Tags.BINARY);
-                result = intConstructor.construct(node);
             } else {
                 throw new YAMLException("Unsupported class: " + type);
             }
diff --git a/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java
index 19d5dc4..fb370f5 100644
--- a/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java
+++ b/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java
@@ -10,7 +10,10 @@
  */
 public abstract class CollectionStartEvent extends NodeEvent {
     private final String tag;
+    // The implicit flag of a collection start event indicates if the tag may be
+    // omitted when the collection is emitted
     private final boolean implicit;
+    // flag indicates if a collection is block or flow
     private final Boolean flowStyle;
 
     public CollectionStartEvent(String anchor, String tag, boolean implicit, Mark startMark,
diff --git a/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
index b76d9b7..c8a1917 100644
--- a/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
+++ b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
@@ -10,8 +10,14 @@
  */
 public final class ScalarEvent extends NodeEvent {
     private final String tag;
+    // style flag of a scalar event indicates the style of the scalar. Possible
+    // values are None, '', '\'', '"', '|', '>'
     private final Character style;
     private final String value;
+    // The implicit flag of a scalar event is a pair of boolean values that
+    // indicate if the tag may be omitted when the scalar is emitted in a plain
+    // and non-plain style correspondingly.
+    //TODO should we use tuple ?
     private final boolean[] implicit;
 
     public ScalarEvent(String anchor, String tag, boolean[] implicit, String value, Mark startMark,
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Node.java b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
index 5a7dfac..abd33fc 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/Node.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
@@ -14,6 +14,11 @@
     protected Mark endMark;
     private Class<? extends Object> type;
     private boolean twoStepsConstruction;
+    // Plain scalars without explicitly defined tag are subject to implicit tag
+    // resolution. The scalar value is checked against a set of regular
+    // expressions.
+    // This is false when the tag was resolved
+    protected boolean explicitTag;
 
     public Node(String tag, Mark startMark, Mark endMark) {
         setTag(tag);
@@ -21,6 +26,7 @@
         this.endMark = endMark;
         this.type = Object.class;
         this.twoStepsConstruction = false;
+        this.explicitTag = false;
     }
 
     public String getTag() {
@@ -78,4 +84,13 @@
     public final int hashCode() {
         return super.hashCode();
     }
+
+    /**
+     * Check if the tag is defined in the YAML document
+     * 
+     * @return true when the tag is explicit, false when the tag is resolved
+     */
+    public boolean hasExplicitTag() {
+        return explicitTag;
+    }
 }
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
index 191c7d7..d669db1 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
@@ -13,12 +13,18 @@
     private String value;
 
     public ScalarNode(String tag, String value, Mark startMark, Mark endMark, Character style) {
+        this(tag, false, value, startMark, endMark, style);
+    }
+
+    public ScalarNode(String tag, boolean explicit, String value, Mark startMark, Mark endMark,
+            Character style) {
         super(tag, startMark, endMark);
         if (value == null) {
             throw new NullPointerException("value in a Node is required.");
         }
         this.value = value;
         this.style = style;
+        this.explicitTag = explicit;
     }
 
     public Character getStyle() {
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
index 30948b4..2f569ee 100644
--- a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
@@ -8,7 +8,10 @@
 
 import junit.framework.TestCase;
 
+import org.yaml.snakeyaml.nodes.Node;
 import org.yaml.snakeyaml.nodes.Tags;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
 
 public class JavaBeanWithNullValuesTest extends TestCase {
     private JavaBeanLoader<JavaBeanWithNullValues> loader;
@@ -20,6 +23,7 @@
 
     public void testNotNull() throws Exception {
         String dumpStr = dumpJavaBeanWithNullValues(false);
+        // System.out.println(dumpStr);
         Yaml yaml = new Yaml();
         JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
         assertNotNull(parsed.getString());
@@ -32,7 +36,6 @@
         assertNotNull(parsed.getSqlDate());
         assertNotNull(parsed.getTimestamp());
         //
-
         parsed = loader.load(dumpStr);
         assertNotNull(parsed.getString());
         assertNotNull(parsed.getBoolean1());
@@ -61,7 +64,7 @@
         options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
         options.setExplicitStart(true);
         options.setExplicitEnd(true);
-        Yaml yaml = new Yaml(options);
+        Yaml yaml = new Yaml(new Dumper(new CustomRepresenter(), options));
         javaBeanWithNullValues.setBoolean1(null);
         javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
         javaBeanWithNullValues.setDouble1(1d);
@@ -73,6 +76,7 @@
         javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
 
         String dumpStr = yaml.dump(javaBeanWithNullValues);
+        // System.out.println(dumpStr);
         yaml = new Yaml();
         JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
         assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
@@ -86,7 +90,7 @@
         options.setExplicitStart(true);
         options.setExplicitEnd(true);
         options.setExplicitRoot(Tags.MAP);
-        Yaml yaml = new Yaml(options);
+        Yaml yaml = new Yaml(new Dumper(new CustomRepresenter(), options));
         javaBeanWithNullValues.setBoolean1(null);
         javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
         javaBeanWithNullValues.setDouble1(1d);
@@ -101,7 +105,7 @@
         // System.out.println(dumpStr);
         assertFalse("No explicit root tag must be used.", dumpStr
                 .contains("JavaBeanWithNullValues"));
-        yaml = new Yaml();
+        yaml = new Yaml(new Dumper(new CustomRepresenter(), options));
         JavaBeanWithNullValues parsed = loader.load(dumpStr);
         assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
         assertNull(" expect null, got " + parsed.getString(), parsed.getString());
@@ -117,7 +121,7 @@
         options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
         options.setExplicitStart(true);
         options.setExplicitEnd(true);
-        Yaml yaml = new Yaml(options);
+        Yaml yaml = new Yaml(new Dumper(new CustomRepresenter(), options));
         if (nullValues) {
             return yaml.dump(javaBeanWithNullValues);
         }
@@ -133,4 +137,38 @@
         return yaml.dump(javaBeanWithNullValues);
     }
 
+    public class CustomRepresenter extends Representer {
+        public CustomRepresenter() {
+            this.representers.put(Float.class, new RepresentFloat());
+            this.representers.put(Long.class, new RepresentLong());
+            this.representers.put(java.sql.Date.class, new RepresentDate());
+            this.representers.put(java.sql.Timestamp.class, new RepresentTime());
+        }
+
+        private class RepresentFloat implements Represent {
+            public Node representData(Object data) {
+                return representScalar(Tags.PREFIX + "java.lang.Float", ((Float) data).toString());
+            }
+        }
+
+        private class RepresentLong implements Represent {
+            public Node representData(Object data) {
+                return representScalar(Tags.PREFIX + "java.lang.Long", ((Long) data).toString());
+            }
+        }
+
+        private class RepresentDate implements Represent {
+            public Node representData(Object data) {
+                return representScalar(Tags.PREFIX + "java.sql.Date", ((java.sql.Date) data)
+                        .toString());
+            }
+        }
+
+        private class RepresentTime implements Represent {
+            public Node representData(Object data) {
+                return representScalar(Tags.PREFIX + "java.sql.Timestamp",
+                        ((java.sql.Timestamp) data).toString());
+            }
+        }
+    }
 }
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java
index 0442d2f..fc734e2 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java
@@ -21,12 +21,19 @@
 import org.yaml.snakeyaml.representer.Representer;

 

 public class YamlMapTest extends TestCase {

+    public void testYaml() throws IOException {

+        Yaml yaml = new Yaml(new Loader(new ExtendedConstructor()), new Dumper(

+                new ExtendedRepresenter(), new DumperOptions()));

+        String output = yaml.dump(new Custom(123));

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

+        Custom o = (Custom) yaml.load(output);

+        assertEquals("123", o.getStr());

+    }

 

     @SuppressWarnings("unchecked")

     public void testYamlMap() throws IOException {

         Map<String, Object> data = new TreeMap<String, Object>();

-        data.put("key3", new Custom("test"));

-        data.put("key4", new Wrapper("test", new Custom("test")));

+        data.put("customTag", new Custom(123));

 

         Yaml yaml = new Yaml(new Loader(new ExtendedConstructor()), new Dumper(

                 new ExtendedRepresenter(), new DumperOptions()));

@@ -36,8 +43,23 @@
 

         assertTrue(o instanceof Map);

         Map<String, Object> m = (Map<String, Object>) o;

-        assertTrue(m.get("key3") instanceof Custom);

-        assertTrue(m.get("key4") instanceof Wrapper);

+        assertTrue(m.get("customTag") instanceof Custom);

+    }

+

+    @SuppressWarnings("unchecked")

+    public void testYamlMapBean() throws IOException {

+        Map<String, Object> data = new TreeMap<String, Object>();

+        data.put("knownClass", new Wrapper("test", new Custom(456)));

+

+        Yaml yaml = new Yaml(new Loader(new ExtendedConstructor()), new Dumper(

+                new ExtendedRepresenter(), new DumperOptions()));

+        String output = yaml.dump(data);

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

+        Object o = yaml.load(output);

+

+        assertTrue(o instanceof Map);

+        Map<String, Object> m = (Map<String, Object>) o;

+        assertEquals(Wrapper.class, m.get("knownClass").getClass());

     }

 

     public static class Wrapper {

@@ -72,12 +94,8 @@
     public static class Custom {

         final private String str;

 

-        public Custom(String s) {

-            str = s;

-        }

-

         public Custom(Integer i) {

-            str = "";

+            str = i.toString();

         }

 

         public Custom(Custom c) {

@@ -87,6 +105,10 @@
         public String toString() {

             return str;

         }

+

+        public String getStr() {

+            return str;

+        }

     }

 

     public static class ExtendedRepresenter extends Representer {

@@ -109,7 +131,7 @@
         private class ConstructCustom extends AbstractConstruct {

             public Object construct(Node node) {

                 String str = (String) constructScalar((ScalarNode) node);

-                return new Custom(str);

+                return new Custom(Integer.parseInt(str));

             }

 

         }

diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java
index beb53e0..b77a89f 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java
@@ -4,36 +4,38 @@
 package org.yaml.snakeyaml.issues.issue9;

 

 import org.yaml.snakeyaml.constructor.Constructor;

-import org.yaml.snakeyaml.error.YAMLException;

 import org.yaml.snakeyaml.nodes.Node;

-import org.yaml.snakeyaml.nodes.NodeId;

 import org.yaml.snakeyaml.nodes.ScalarNode;

 

 public class BeanConstructor extends Constructor {

 

     public BeanConstructor() {

         super(BeanHolder.class);

-        yamlClassConstructors.put(NodeId.scalar, new BeanScalarConstructor());

+

+        // TODO the tag may start with !!

+        // yamlConstructors.put("tag:yaml.org,2002:org.yaml.snakeyaml.issues.issue9.Bean1",

+        yamlConstructors.put("tag:yaml.org,2002:org.yaml.snakeyaml.issues.issue9.Bean1",

+                new Bean1ScalarConstructor());

+        yamlConstructors.put("tag:yaml.org,2002:org.yaml.snakeyaml.issues.issue9.BeanHolder",

+                new BeanHolderScalarConstructor());

     }

 

-    private class BeanScalarConstructor extends ConstructScalar {

+    private class Bean1ScalarConstructor extends ConstructScalar {

         @Override

         public Object construct(Node node) {

             ScalarNode snode = (ScalarNode) node;

-            if (BeanHolder.class.equals(node.getType()) || IBean.class.equals(node.getType())) {

-                if (snode.getValue().length() == 0) {

-                    try {

-                        return getClassForNode(node).newInstance();

-                    } catch (Exception e) {

-                        throw new YAMLException("BeanHolder cannot be created");

-                    }

-                } else {

-                    throw new YAMLException("BeanHolder cannot be created out of '"

-                            + snode.getValue() + "'");

-                }

+            if (snode.getValue().length() == 0) {

+                return new Bean1();

             } else {

-                return super.construct(node);

+                return new Bean1(Integer.parseInt(snode.getValue()));

             }

         }

     }

+

+    private class BeanHolderScalarConstructor extends ConstructScalar {

+        @Override

+        public Object construct(Node node) {

+            return new BeanHolder();

+        }

+    }

 }

diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
index 20a8377..462a8be 100644
--- a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
@@ -44,16 +44,19 @@
     }

 

     /**

-     * Runtime class is more important then the tag (which is ignored if the

-     * class is known)

+     * Runtime class has less priority then an explicit tag

      */

     public void testClassAndTag() {

         String output = "name: !!whatever Bean25\nshape: !!org.yaml.snakeyaml.javabeans.Triangle\n  name: Triangle25\n";

         JavaBeanLoader<TriangleBean> beanLoader = new JavaBeanLoader<TriangleBean>(

                 TriangleBean.class);

-        TriangleBean loadedBean = beanLoader.load(output);

-        assertNotNull(loadedBean);

-        assertEquals("Bean25", loadedBean.getName());

-        assertEquals(7, loadedBean.getShape().process());

+        try {

+            beanLoader.load(output);

+            fail("Runtime class has less priority then an explicit tag");

+        } catch (Exception e) {

+            assertEquals(

+                    "Cannot create property=name for JavaBean=TriangleBean name=null; null; Can't construct a java object for tag:yaml.org,2002:whatever; exception=Class not found: whatever",

+                    e.getMessage());

+        }

     }

 }