Issue 84: JavaBeanLoader does not use regular expressions for implicit types
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index bf5cc46..0b41f05 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,10 @@
 	</properties>

 	<body>

           <release version="1.8-SNAPSHOT" date="in Mercurial" description="Performance improvement">

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

+                JavaBeanLoader does not use regular expressions for implicit types. It is possible

+                to keep the implicit types when it is required. (2010-09-06)

+            </action>

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

                 Introduce LoaderOptions to be able to specify configuration while loading (2010-09-03)

             </action>

diff --git a/src/main/java/org/yaml/snakeyaml/JavaBeanLoader.java b/src/main/java/org/yaml/snakeyaml/JavaBeanLoader.java
index 46b327f..e5f0a83 100644
--- a/src/main/java/org/yaml/snakeyaml/JavaBeanLoader.java
+++ b/src/main/java/org/yaml/snakeyaml/JavaBeanLoader.java
@@ -23,6 +23,8 @@
 import org.yaml.snakeyaml.constructor.Constructor;

 import org.yaml.snakeyaml.introspector.BeanAccess;

 import org.yaml.snakeyaml.reader.UnicodeReader;

+import org.yaml.snakeyaml.representer.Representer;

+import org.yaml.snakeyaml.resolver.Resolver;

 

 /**

  * Convenience utility to parse JavaBeans. When the YAML document contains a

@@ -38,14 +40,25 @@
         this(typeDescription, BeanAccess.DEFAULT);

     }

 

+    // TODO should BeanAccess be inside LoaderOptions ???

     public JavaBeanLoader(TypeDescription typeDescription, BeanAccess beanAccess) {

-        if (typeDescription == null) {

+        this(new LoaderOptions(typeDescription), beanAccess);

+    }

+

+    public JavaBeanLoader(LoaderOptions options, BeanAccess beanAccess) {

+        if (options.getRootTypeDescription() == null) {

             throw new NullPointerException("TypeDescription must be provided.");

         }

-        Constructor constructor = new Constructor(typeDescription.getType());

-        typeDescription.setRoot(true);

-        constructor.addTypeDescription(typeDescription);

-        loader = new Yaml(constructor);

+        Constructor constructor = new Constructor(options.getRootTypeDescription().getType());

+        options.getRootTypeDescription().setRoot(true);

+        Resolver resolver;

+        if (options.useImplicitTypes()) {

+            resolver = new Resolver(Resolver.Mode.STANDARD);

+        } else {

+            resolver = new Resolver(Resolver.Mode.JAVABEAN);

+        }

+        constructor.addTypeDescription(options.getRootTypeDescription());

+        loader = new Yaml(constructor, options, new Representer(), new DumperOptions(), resolver);

         loader.setBeanAccess(beanAccess);

     }

 

diff --git a/src/main/java/org/yaml/snakeyaml/LoaderOptions.java b/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
index edf2ce7..9f7e24f 100644
--- a/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
+++ b/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
@@ -22,7 +22,16 @@
      * 40% slower and it consumes much more memory (default=false)
      */
     private boolean withMarkContext = false;
-    private ImplicitMode implicitMode = ImplicitMode.ALWAYS_IMPLICIT_TYPES;
+    private ImplicitMode implicitMode = ImplicitMode.DYNAMIC_IMPLICIT_TYPES;
+    private TypeDescription rootTypeDescription;
+
+    public LoaderOptions() {
+        this(new TypeDescription(Object.class));
+    }
+
+    public LoaderOptions(TypeDescription rootTypeDescription) {
+        this.rootTypeDescription = rootTypeDescription;
+    }
 
     public enum ImplicitMode {
         /**
@@ -55,4 +64,21 @@
     public void setImplicitMode(ImplicitMode implicitMode) {
         this.implicitMode = implicitMode;
     }
+
+    public TypeDescription getRootTypeDescription() {
+        return rootTypeDescription;
+    }
+
+    public void setRootTypeDescription(TypeDescription rootTypeDescription) {
+        this.rootTypeDescription = rootTypeDescription;
+    }
+
+    public boolean useImplicitTypes() {
+        if (implicitMode == ImplicitMode.DYNAMIC_IMPLICIT_TYPES) {
+            Class<?> t = rootTypeDescription.getType();
+            return Object.class.equals(t);
+        } else {
+            return implicitMode == ImplicitMode.ALWAYS_IMPLICIT_TYPES;
+        }
+    }
 }
diff --git a/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java b/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java
index ecc2fa9..98aaba0 100644
--- a/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java
+++ b/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java
@@ -48,6 +48,10 @@
 
     protected Map<Character, List<ResolverTuple>> yamlImplicitResolvers = new HashMap<Character, List<ResolverTuple>>();
 
+    public enum Mode {
+        STANDARD, JAVABEAN;
+    }
+
     /**
      * Create Resolver
      * 
@@ -78,7 +82,16 @@
     }
 
     public Resolver() {
-        this(true);
+        this(Mode.STANDARD);
+    }
+
+    public Resolver(Mode mode) {
+        if (mode == Mode.STANDARD) {
+            addImplicitResolvers();
+        } else {
+            addImplicitResolver(Tag.NULL, NULL, "~nN\0");
+            addImplicitResolver(Tag.NULL, EMPTY, null);
+        }
     }
 
     public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
diff --git a/src/test/java/examples/collections/TypeSafeMapTest.java b/src/test/java/examples/collections/TypeSafeMapTest.java
index 83d9d59..440a798 100644
--- a/src/test/java/examples/collections/TypeSafeMapTest.java
+++ b/src/test/java/examples/collections/TypeSafeMapTest.java
@@ -23,7 +23,11 @@
 

 import org.yaml.snakeyaml.JavaBeanDumper;

 import org.yaml.snakeyaml.JavaBeanLoader;

+import org.yaml.snakeyaml.LoaderOptions;

+import org.yaml.snakeyaml.TypeDescription;

 import org.yaml.snakeyaml.Util;

+import org.yaml.snakeyaml.LoaderOptions.ImplicitMode;

+import org.yaml.snakeyaml.introspector.BeanAccess;

 

 /**

  * Test MapBean->Map<String, Developer> developers <br/>

@@ -166,8 +170,10 @@
     public void testLoadMapWithObject() {

         String output = Util.getLocalResource("examples/map-bean-10.yaml");

         // System.out.println(output);

+        LoaderOptions options = new LoaderOptions(new TypeDescription(MapBeanNoGenerics.class));

+        options.setImplicitMode(ImplicitMode.ALWAYS_IMPLICIT_TYPES);

         JavaBeanLoader<MapBeanNoGenerics> beanLoader = new JavaBeanLoader<MapBeanNoGenerics>(

-                MapBeanNoGenerics.class);

+                options, BeanAccess.DEFAULT);

         MapBeanNoGenerics parsed = beanLoader.load(output);

         assertNotNull(parsed);

         Map<String, Integer> data = parsed.getData();

diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanLoaderTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanLoaderTest.java
index 0b13cd4..654c2d5 100644
--- a/src/test/java/org/yaml/snakeyaml/JavaBeanLoaderTest.java
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanLoaderTest.java
@@ -152,6 +152,23 @@
         assertEquals("Name123", parsed.getName());

     }

 

+    public void testTypeDescription1111() {

+        Bean3 bean3 = new Bean3();

+        bean3.setName("Name123");

+        Bean bean = new Bean();

+        bean.setId(3);

+        bean.setName("Test me.");

+        bean3.setBean(bean);

+        JavaBeanDumper dumper = new JavaBeanDumper();

+        String output = dumper.dump(bean3);

+        assertEquals("bean:\n  id: 3\n  name: Test me.\nlist: null\nname: Name123\n", output);

+        TypeDescription td = new TypeDescription(Bean3.class);

+        td.putListPropertyType("list", Integer.class);

+        JavaBeanLoader<Bean3> loader = new JavaBeanLoader<Bean3>(td);

+        Bean3 parsed = loader.load("bean:\n  id: 3\n  name: Test me.\n\nname: Name123\n");

+        assertEquals("Name123", parsed.getName());

+    }

+

     public void testTypeDescription2() {

         Bean3 bean3 = new Bean3();

         bean3.setName("Name123");

diff --git a/src/test/java/org/yaml/snakeyaml/LoaderOptionsTest.java b/src/test/java/org/yaml/snakeyaml/LoaderOptionsTest.java
index f722395..4d902a3 100644
--- a/src/test/java/org/yaml/snakeyaml/LoaderOptionsTest.java
+++ b/src/test/java/org/yaml/snakeyaml/LoaderOptionsTest.java
@@ -18,12 +18,38 @@
 

 import junit.framework.TestCase;

 

+import org.yaml.snakeyaml.LoaderOptions.ImplicitMode;

+

 public class LoaderOptionsTest extends TestCase {

 

     public void testGetMode() {

         LoaderOptions defaultOptions = new LoaderOptions();

         assertFalse(defaultOptions.isWithMarkContext());

-        assertEquals(LoaderOptions.ImplicitMode.ALWAYS_IMPLICIT_TYPES, defaultOptions

+        assertEquals(LoaderOptions.ImplicitMode.DYNAMIC_IMPLICIT_TYPES, defaultOptions

                 .getImplicitMode());

     }

+

+    public void testUseImplicitTypes1() {

+        LoaderOptions options = new LoaderOptions();

+        assertTrue(options.useImplicitTypes());

+

+    }

+

+    public void testUseImplicitTypes2() {

+        LoaderOptions options = new LoaderOptions(new TypeDescription(Object.class));

+        assertTrue(options.useImplicitTypes());

+    }

+

+    public void testUseImplicitTypes3() {

+        LoaderOptions options = new LoaderOptions(new TypeDescription(LoaderOptionsTest.class));

+        assertFalse(options.useImplicitTypes());

+    }

+

+    public void testMoImplicitTypes() {

+        LoaderOptions options = new LoaderOptions();

+        options.setImplicitMode(ImplicitMode.NEVER_IMPLICIT_TYPES);

+        Yaml yaml = new Yaml(options);

+        // String number = (String) yaml.load("11");

+        // TODO assertEquals("Integer must not be recognised.", "11", number);

+    }

 }

diff --git a/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
index ce18cf3..d42539d 100644
--- a/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
+++ b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
@@ -209,7 +209,20 @@
             yaml.load(document);

             fail("ExceptionParent should not be created.");

         } catch (Exception e) {

-            assertTrue(e.getMessage().contains(

+            assertTrue(e.getMessage(), e.getMessage().contains(

+                    "Can't construct a java object for scalar tag:yaml.org,2002:int"));

+        }

+    }

+

+    public void testScalarContructorExceptionNoRegExpMode() throws IOException {

+        //TODO ???

+        Yaml yaml = new Yaml(new Constructor(ExceptionParent.class));

+        String document = "id: 123\nchild: 25";

+        try {

+            yaml.load(document);

+            fail("ExceptionParent should not be created.");

+        } catch (Exception e) {

+            assertTrue(e.getMessage(), e.getMessage().contains(

                     "Can't construct a java object for scalar tag:yaml.org,2002:int"));

         }

     }

diff --git a/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java
index 071db15..9dc3fe6 100644
--- a/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java
+++ b/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java
@@ -69,6 +69,26 @@
         }

     }

 

+    public void testTypeSafeMapNoRegularExpressionsMode() throws IOException {

+        Constructor constructor = new Constructor(MyCar.class);

+        TypeDescription carDescription = new TypeDescription(MyCar.class);

+        carDescription.putMapPropertyType("wheels", MyWheel.class, Object.class);

+        constructor.addTypeDescription(carDescription);

+        Yaml yaml = new Yaml(constructor);

+        MyCar car = (MyCar) yaml.load(Util

+                .getLocalResource("constructor/car-no-root-class-map.yaml"));

+        assertEquals("00-FF-Q2", car.getPlate());

+        Map<MyWheel, Date> wheels = car.getWheels();

+        assertNotNull(wheels);

+        assertEquals(5, wheels.size());

+        for (MyWheel wheel : wheels.keySet()) {

+            assertTrue(wheel.getId() > 0);

+            Date date = wheels.get(wheel);

+            long time = date.getTime();

+            assertTrue("It must be midnight.", time % 10000 == 0);

+        }

+    }

+

     public void testWithGlobalTag() throws IOException {

         Map<MyWheel, Date> wheels = new TreeMap<MyWheel, Date>();

         long time = 1248212168084L;

diff --git a/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java
index 9360322..c2f805c 100644
--- a/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java
+++ b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java
@@ -25,6 +25,10 @@
 
 import org.yaml.snakeyaml.JavaBeanDumper;
 import org.yaml.snakeyaml.JavaBeanLoader;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.LoaderOptions.ImplicitMode;
+import org.yaml.snakeyaml.introspector.BeanAccess;
 
 public class ObjectValuesTest extends TestCase {
 
@@ -48,8 +52,10 @@
 
         JavaBeanDumper dumper = new JavaBeanDumper();
         String dumpedStr = dumper.dump(ov);
-
-        JavaBeanLoader<ObjectValues> loader = new JavaBeanLoader<ObjectValues>(ObjectValues.class);
+        LoaderOptions options = new LoaderOptions(new TypeDescription(ObjectValues.class));
+        options.setImplicitMode(ImplicitMode.ALWAYS_IMPLICIT_TYPES);
+        JavaBeanLoader<ObjectValues> loader = new JavaBeanLoader<ObjectValues>(options,
+                BeanAccess.DEFAULT);
         ObjectValues ov2 = loader.load(dumpedStr);
 
         assertEquals(ov.getObject(), ov2.getObject());
@@ -58,7 +64,6 @@
         ov.getPossible()[0] = ov2.getPossible()[0];
     }
 
-    @SuppressWarnings("unchecked")
     public void testObjectValuesWithParam() {
         ObjectValuesWithParam<String, Integer> ov = new ObjectValuesWithParam<String, Integer>();
         Integer obj = new Integer(131313);
@@ -80,8 +85,11 @@
         JavaBeanDumper dumper = new JavaBeanDumper();
         String dumpedStr = dumper.dump(ov);
 
+        LoaderOptions options = new LoaderOptions(new TypeDescription(
+                new ObjectValuesWithParam<String, Integer>().getClass()));
+        options.setImplicitMode(ImplicitMode.ALWAYS_IMPLICIT_TYPES);
         JavaBeanLoader<ObjectValuesWithParam<String, Integer>> loader = new JavaBeanLoader<ObjectValuesWithParam<String, Integer>>(
-                new ObjectValuesWithParam<String, Integer>().getClass());
+                options, BeanAccess.DEFAULT);
         ObjectValuesWithParam<String, Integer> ov2 = loader.load(dumpedStr);
 
         assertEquals(ov.getObject(), ov2.getObject());
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java
index dc7343b..3a68b84 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java
@@ -23,7 +23,11 @@
 

 import org.yaml.snakeyaml.JavaBeanDumper;

 import org.yaml.snakeyaml.JavaBeanLoader;

+import org.yaml.snakeyaml.LoaderOptions;

+import org.yaml.snakeyaml.TypeDescription;

 import org.yaml.snakeyaml.Util;

+import org.yaml.snakeyaml.LoaderOptions.ImplicitMode;

+import org.yaml.snakeyaml.introspector.BeanAccess;

 

 public class GenericMapBeanTest extends TestCase {

     @SuppressWarnings("unchecked")

@@ -36,7 +40,11 @@
         // System.out.println(s);

         assertEquals("map:\n  foo: 17\n  bar: 19\n", s);

         // parse

-        JavaBeanLoader<MapProvider> loader = new JavaBeanLoader<MapProvider>(MapProvider.class);

+        LoaderOptions options = new LoaderOptions();

+        options.setRootTypeDescription(new TypeDescription(MapProvider.class));

+        options.setImplicitMode(ImplicitMode.ALWAYS_IMPLICIT_TYPES);

+        JavaBeanLoader<MapProvider> loader = new JavaBeanLoader<MapProvider>(options,

+                BeanAccess.DEFAULT);

         MapProvider<String, Integer> listProvider2 = loader.load(s);

         assertEquals(new Integer(17), listProvider2.getMap().get("foo"));

         assertEquals(new Integer(19), listProvider2.getMap().get("bar"));

@@ -44,6 +52,27 @@
     }

 

     @SuppressWarnings("unchecked")

+    public void testGenericMapNoImplicitTypes() throws Exception {

+        JavaBeanDumper beanDumper = new JavaBeanDumper();

+        MapProvider<String, Integer> listProvider = new MapProvider<String, Integer>();

+        listProvider.getMap().put("foo", 17);

+        listProvider.getMap().put("bar", 19);

+        String s = beanDumper.dump(listProvider);

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

+        assertEquals("map:\n  foo: 17\n  bar: 19\n", s);

+        // parse

+        LoaderOptions options = new LoaderOptions();

+        options.setRootTypeDescription(new TypeDescription(MapProvider.class));

+        options.setImplicitMode(ImplicitMode.NEVER_IMPLICIT_TYPES);

+        JavaBeanLoader<MapProvider> loader = new JavaBeanLoader<MapProvider>(options,

+                BeanAccess.DEFAULT);

+        MapProvider<String, Integer> listProvider2 = loader.load(s);

+        assertEquals("17", listProvider2.getMap().get("foo"));

+        assertEquals("19", listProvider2.getMap().get("bar"));

+        assertFalse(listProvider.equals(listProvider2));

+    }

+

+    @SuppressWarnings("unchecked")

     public void testGenericBean() throws Exception {

         JavaBeanDumper beanDumper = new JavaBeanDumper();

         MapProvider<String, Bean> listProvider = new MapProvider<String, Bean>();

diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java
index e1a913f..65b8635 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java
@@ -81,7 +81,7 @@
             CustomerAB parsed = (CustomerAB) yaml.load(dump);
         } catch (Exception e) {
             // TODO fix issue 82
-            e.printStackTrace();
+            // e.printStackTrace();
         }
     }
 
@@ -108,7 +108,7 @@
             CustomerAB parsed = loader.load(dump2);
         } catch (Exception e) {
             // TODO fix issue 82
-            e.printStackTrace();
+            // e.printStackTrace();
         }
 
     }