OSDN Git Service

Resolved: http://code.google.com/p/xerial/issues/detail?id=29
authorleo <leo@ae02f08e-27ec-0310-ae8c-8ba02fe2eafd>
Fri, 11 Sep 2009 03:09:47 +0000 (03:09 +0000)
committerleo <leo@ae02f08e-27ec-0310-ae8c-8ba02fe2eafd>
Fri, 11 Sep 2009 03:09:47 +0000 (03:09 +0000)
git-svn-id: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core@3578 ae02f08e-27ec-0310-ae8c-8ba02fe2eafd

src/main/java/org/xerial/lens/ObjectLens.java
src/main/java/org/xerial/lens/ObjectMapper.java
src/main/java/org/xerial/lens/impl/RelationSetter.java
src/test/java/org/xerial/lens/ObjectLensTest.java
src/test/java/org/xerial/lens/map.silk [new file with mode: 0644]

index 7a0901d..84bbc8a 100644 (file)
@@ -116,7 +116,7 @@ public class ObjectLens {
 \r
     protected ObjectLens(Class< ? > targetType) {\r
         this.targetType = targetType;\r
-        createBindRules(targetType);\r
+        prepareBindRules(targetType);\r
     }\r
 \r
     public Class< ? > getTargetType() {\r
@@ -128,7 +128,7 @@ public class ObjectLens {
         return String.format("(%s, %s)", setterContainer, relationSetterContainer);\r
     }\r
 \r
-    private void createBindRules(Class< ? > targetType) {\r
+    private void prepareBindRules(Class< ? > targetType) {\r
         // search for object parameters including superclass's ones \r
 \r
         // scan public fields\r
@@ -138,6 +138,7 @@ public class ObjectLens {
             if (parentClassOfTheField == Object.class)\r
                 continue;\r
 \r
+            // looking for only public fields\r
             int fieldModifier = eachField.getModifiers();\r
             if (Modifier.isPublic(fieldModifier) && !Modifier.isTransient(fieldModifier)\r
                     && !Modifier.isStatic(fieldModifier)) {\r
@@ -150,15 +151,21 @@ public class ObjectLens {
                     continue;\r
                 }\r
                 else if (TypeInfo.isMap(fieldType)) {\r
-                    // TODO map putter\r
-                    Pair<Type, Type> keyValueTypes = ReflectionUtil\r
-                            .getGenericMapElementType(eachField);\r
-                    Pair<String, String> keyValueNames = pickRelationName(eachField.getName());\r
-                    if (keyValueNames == null) {\r
-                        // infer key and value names from type parameters in Map<Key, Value>\r
+                    Pair<String, String> keyValueName = pickRelationName(eachField.getName());\r
+                    if (keyValueName == null) {\r
+                        // infer key, value names from the class type\r
+                        Pair<Type, Type> mapElementType = ReflectionUtil\r
+                                .getGenericMapElementType(eachField);\r
 \r
+                        Class< ? > keyType = Class.class.cast(mapElementType.getFirst());\r
+                        Class< ? > valueType = Class.class.cast(mapElementType.getSecond());\r
+\r
+                        keyValueName = new Pair<String, String>(keyType.getSimpleName(), valueType\r
+                                .getSimpleName());\r
                     }\r
 \r
+                    relationSetterContainer.add(RelationSetter.newMapSetter(\r
+                            keyValueName.getFirst(), keyValueName.getSecond(), eachField));\r
                 }\r
                 else if (TypeInfo.isCollection(fieldType)) {\r
                     Class< ? > elementType = ReflectionUtil.getRawClass(ReflectionUtil\r
index b5abaf5..5de09eb 100644 (file)
@@ -61,16 +61,16 @@ import org.xerial.util.tree.TreeParser;
 public class ObjectMapper {
     private static Logger _logger = Logger.getLogger(ObjectMapper.class);
 
-    //-------------------------------------
-    // dynamic parameters required for mapping tree-structured data to an object
     // id -> corresponding object instance
-    //-------------------------------------
     private HashMap<Long, Object> objectHolder = new HashMap<Long, Object>();
-    private Deque<Object> contextNodeStack = new ArrayDeque<Object>();
 
-    private static QuerySet qs = null;
     // schema -> binder 
-    private static HashMap<Schema, Binder> schema2binder = new HashMap<Schema, Binder>();
+    private HashMap<Schema, Binder> schema2binder = new HashMap<Schema, Binder>();
+
+    private Deque<Object> contextNodeStack = new ArrayDeque<Object>();
+    private final QuerySet qs;
+
+    private static HashMap<Class< ? >, ObjectMapper> prebuiltMapper = new HashMap<Class< ? >, ObjectMapper>();
 
     /**
      * interface for invoking setters or field setters of the object
@@ -171,14 +171,81 @@ public class ObjectMapper {
         }
     }
 
+    public <T> ObjectMapper(Class<T> targetType) throws XerialException {
+        qs = buildQuery(targetType);
+    }
+
+    public static ObjectMapper getMapper(Class< ? > targetType) throws XerialException {
+        if (prebuiltMapper.containsKey(targetType))
+            return prebuiltMapper.get(targetType);
+        else {
+            ObjectMapper newInstance = new ObjectMapper(targetType);
+            prebuiltMapper.put(targetType, newInstance);
+            return newInstance;
+        }
+    }
+
+    public <T> T map(Class<T> targetType, TreeParser parser) throws XerialException {
+
+        T object = TypeInfo.createInstance(targetType);
+        return map(object, parser);
+    }
+
+    public <T> T map(T object, TreeParser parser) throws XerialException {
+        try {
+            if (object == null)
+                throw new XerialError(XerialErrorCode.INVALID_INPUT, "null object");
+
+            if (_logger.isTraceEnabled())
+                _logger.trace("query set: " + qs);
+
+            // set the root object
+            objectHolder.put(0L, object);
+            contextNodeStack.addLast(object);
+
+            AmoebaJoinHandler mapper = new RelationExtracter();
+
+            StreamAmoebaJoin aj = new StreamAmoebaJoin(qs, mapper);
+            aj.sweep(parser);
+            return object;
+        }
+        catch (IOException e) {
+            throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);
+        }
+        catch (Exception e) {
+            throw new XerialException(XerialErrorCode.INHERITED, e);
+        }
+
+    }
+
+    public <T> void find(Class<T> targetType, TreeParser parser,
+            String coreNodeNameOfTheTargetType, ObjectHandler<T> handler) throws XerialException {
+
+        try {
+            AmoebaJoinHandler mapper = new RelationExtracter();
+
+            _logger.info(qs);
+
+            StreamAmoebaJoin aj = new StreamAmoebaJoin(qs, mapper);
+            aj.sweep(parser);
+        }
+        catch (IOException e) {
+            throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);
+        }
+        catch (Exception e) {
+            throw new XerialException(XerialErrorCode.INHERITED, e);
+        }
+
+    }
+
     private QuerySet buildQuery(Class< ? > targetType) {
         QueryBuilder qb = new QueryBuilder();
         qb.build(targetType, "root");
-        return qb.qsBuilder.build();
+        return qb.qs.build();
     }
 
     private class QueryBuilder {
-        QuerySetBuilder qsBuilder = new QuerySetBuilder();
+        QuerySetBuilder qs = new QuerySetBuilder();
         private HashMap<String, Set<Class< ? >>> processedClassTable = new HashMap<String, Set<Class< ? >>>();
 
         //Set<Class< ? >> processedClasses = new HashSet<Class< ? >>();
@@ -219,7 +286,7 @@ public class ObjectMapper {
                     builder.add("entry");
                     builder.add(each.getParameterName());
                     Schema s = builder.build();
-                    qsBuilder.addQueryTarget(s);
+                    qs.addQueryTarget(s);
                     schema2binder.put(s, new AttributeBinder(MapEntry.class, each));
                     continue;
                 }
@@ -231,7 +298,7 @@ public class ObjectMapper {
                 builder.add(each.getParameterName());
 
                 Schema s = builder.build();
-                qsBuilder.addQueryTarget(s);
+                qs.addQueryTarget(s);
 
                 schema2binder.put(s, new AttributeBinder(lens.getTargetType(), each));
             }
@@ -242,7 +309,7 @@ public class ObjectMapper {
 
                 Schema s = new SchemaBuilder().add(each.getCoreNodeName()).add(
                         each.getAttributeNodeName()).build();
-                qsBuilder.addQueryTarget(s);
+                qs.addQueryTarget(s);
 
                 schema2binder.put(s, new RelationBinder(lens, each));
 
@@ -252,70 +319,6 @@ public class ObjectMapper {
 
     }
 
-    public <T> ObjectMapper(Class<T> targetType) throws XerialException {
-        if (qs == null) {
-            schema2binder.clear();
-            qs = buildQuery(targetType);
-        }
-    }
-
-    public static ObjectMapper getMapper(Class< ? > targetType) throws XerialException {
-        return new ObjectMapper(targetType);
-    }
-
-    public <T> T map(Class<T> targetType, TreeParser parser) throws XerialException {
-
-        T object = TypeInfo.createInstance(targetType);
-        return map(object, parser);
-    }
-
-    public <T> T map(T object, TreeParser parser) throws XerialException {
-        try {
-            if (object == null)
-                throw new XerialError(XerialErrorCode.INVALID_INPUT, "null object");
-
-            if (_logger.isTraceEnabled())
-                _logger.trace("query set: " + qs);
-
-            // set the root object
-            objectHolder.put(0L, object);
-            contextNodeStack.addLast(object);
-
-            AmoebaJoinHandler mapper = new RelationExtracter();
-
-            StreamAmoebaJoin aj = new StreamAmoebaJoin(qs, mapper);
-            aj.sweep(parser);
-            return object;
-        }
-        catch (IOException e) {
-            throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);
-        }
-        catch (Exception e) {
-            throw new XerialException(XerialErrorCode.INHERITED, e);
-        }
-
-    }
-
-    public <T> void find(Class<T> targetType, TreeParser parser,
-            String coreNodeNameOfTheTargetType, ObjectHandler<T> handler) throws XerialException {
-
-        try {
-            AmoebaJoinHandler mapper = new RelationExtracter();
-
-            _logger.info(qs);
-
-            StreamAmoebaJoin aj = new StreamAmoebaJoin(qs, mapper);
-            aj.sweep(parser);
-        }
-        catch (IOException e) {
-            throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);
-        }
-        catch (Exception e) {
-            throw new XerialException(XerialErrorCode.INHERITED, e);
-        }
-
-    }
-
     private Object getNodeInstance(Node node, Class< ? > nodeType) throws XerialException {
         Object instance = objectHolder.get(node.nodeID);
         if (instance != null)
index 5742479..15469b8 100644 (file)
 //--------------------------------------\r
 package org.xerial.lens.impl;\r
 \r
+import java.lang.reflect.Field;\r
 import java.lang.reflect.InvocationTargetException;\r
 import java.lang.reflect.Method;\r
+import java.lang.reflect.Type;\r
+import java.util.Map;\r
 \r
 import org.xerial.core.XerialError;\r
 import org.xerial.core.XerialErrorCode;\r
 import org.xerial.core.XerialException;\r
+import org.xerial.util.Pair;\r
+import org.xerial.util.bean.TypeInfo;\r
+import org.xerial.util.reflect.ReflectionUtil;\r
 \r
 /**\r
  * RelationSetter is for setting a node tuple to an object\r
@@ -37,53 +43,45 @@ import org.xerial.core.XerialException;
  * @author leo\r
  * \r
  */\r
-public abstract class RelationSetter\r
-{\r
+public abstract class RelationSetter {\r
     private final Class< ? > coreNodeType;\r
     private final Class< ? > attributeNodeType;\r
 \r
     private final String coreNodeName;\r
     private final String attributeNodeName;\r
 \r
-    protected RelationSetter(Class< ? > coreNodeType, String coreNodeName, Class< ? > attributeNodeType,\r
-            String attributeNodeName)\r
-    {\r
+    protected RelationSetter(Class< ? > coreNodeType, String coreNodeName,\r
+            Class< ? > attributeNodeType, String attributeNodeName) {\r
         this.coreNodeType = coreNodeType;\r
         this.attributeNodeType = attributeNodeType;\r
         this.coreNodeName = coreNodeName;\r
         this.attributeNodeName = attributeNodeName;\r
     }\r
 \r
-    public Class< ? > getCoreNodeType()\r
-    {\r
+    public Class< ? > getCoreNodeType() {\r
         return coreNodeType;\r
     }\r
 \r
-    public Class< ? > getAttributeNodeType()\r
-    {\r
+    public Class< ? > getAttributeNodeType() {\r
         return attributeNodeType;\r
     }\r
 \r
-    public String getCoreNodeName()\r
-    {\r
+    public String getCoreNodeName() {\r
         return coreNodeName;\r
     }\r
 \r
-    public String getAttributeNodeName()\r
-    {\r
+    public String getAttributeNodeName() {\r
         return attributeNodeName;\r
     }\r
 \r
     @Override\r
-    public String toString()\r
-    {\r
-        return String.format("(%s[%s], %s[%s])", coreNodeName, coreNodeType.getSimpleName(), attributeNodeName,\r
-                attributeNodeType.getSimpleName());\r
+    public String toString() {\r
+        return String.format("(%s[%s], %s[%s])", coreNodeName, coreNodeType.getSimpleName(),\r
+                attributeNodeName, attributeNodeType.getSimpleName());\r
     }\r
 \r
     @Override\r
-    public boolean equals(Object obj)\r
-    {\r
+    public boolean equals(Object obj) {\r
         RelationSetter other = RelationSetter.class.cast(obj);\r
         if (other == null)\r
             return false;\r
@@ -92,53 +90,110 @@ public abstract class RelationSetter
     }\r
 \r
     @Override\r
-    public int hashCode()\r
-    {\r
+    public int hashCode() {\r
         return coreNodeName.hashCode() + attributeNodeName.hashCode();\r
     }\r
 \r
-    public abstract void bind(Object object, Object coreValue, Object attributeValue) throws XerialException;\r
+    public abstract void bind(Object object, Object coreValue, Object attributeValue)\r
+            throws XerialException;\r
 \r
-    public static RelationSetter newRelationSetter(String coreNodeName, String attributeNodeName, Method setterMethod)\r
-    {\r
+    public static RelationSetter newRelationSetter(String coreNodeName, String attributeNodeName,\r
+            Method setterMethod) {\r
         Class< ? >[] argType = setterMethod.getParameterTypes();\r
         if (argType.length != 2)\r
             throw new XerialError(XerialErrorCode.INVALID_INPUT, setterMethod.toString());\r
-        return new MethodRelationSetter(argType[0], coreNodeName, argType[1], attributeNodeName, setterMethod);\r
+        return new MethodRelationSetter(argType[0], coreNodeName, argType[1], attributeNodeName,\r
+                setterMethod);\r
     }\r
 \r
-    private static class MethodRelationSetter extends RelationSetter\r
-    {\r
+    private static class MethodRelationSetter extends RelationSetter {\r
         private final Method setter;\r
 \r
-        public MethodRelationSetter(Class< ? > coreNodeType, String coreNodeName, Class< ? > attributeNodeType,\r
-                String attributeNodeName, Method setter)\r
-        {\r
+        public MethodRelationSetter(Class< ? > coreNodeType, String coreNodeName,\r
+                Class< ? > attributeNodeType, String attributeNodeName, Method setter) {\r
             super(coreNodeType, coreNodeName, attributeNodeType, attributeNodeName);\r
             this.setter = setter;\r
         }\r
 \r
         @Override\r
-        public void bind(Object object, Object coreValue, Object attributeValue) throws XerialException\r
-        {\r
-            try\r
-            {\r
+        public void bind(Object object, Object coreValue, Object attributeValue)\r
+                throws XerialException {\r
+            try {\r
                 setter.invoke(object, coreValue, attributeValue);\r
             }\r
-            catch (IllegalArgumentException e)\r
-            {\r
+            catch (IllegalArgumentException e) {\r
                 throw new XerialException(XerialErrorCode.WRONG_DATA_TYPE, e);\r
             }\r
-            catch (IllegalAccessException e)\r
-            {\r
+            catch (IllegalAccessException e) {\r
                 throw new XerialException(XerialErrorCode.INVALID_INPUT, e);\r
             }\r
-            catch (InvocationTargetException e)\r
-            {\r
+            catch (InvocationTargetException e) {\r
                 throw new XerialError(XerialErrorCode.WRONG_DATA_TYPE, e);\r
             }\r
 \r
         }\r
     }\r
 \r
+    public static RelationSetter newMapSetter(String keyNodeName, String valueNodeName,\r
+            Field mapField) {\r
+        Pair<Type, Type> mapElementType = ReflectionUtil.getGenericMapElementType(mapField);\r
+\r
+        Class< ? > keyType = Class.class.cast(mapElementType.getFirst());\r
+        Class< ? > valueType = Class.class.cast(mapElementType.getSecond());\r
+\r
+        return new MapFieldSetter(keyType, keyNodeName, valueType, valueNodeName, mapField);\r
+    }\r
+\r
+    private static class MapFieldSetter extends RelationSetter {\r
+        private final Field mapField;\r
+        private final ParameterGetter mapTypeGetter;\r
+        private final Method putter;\r
+\r
+        public MapFieldSetter(Class< ? > coreNodeType, String coreNodeName,\r
+                Class< ? > attributeNodeType, String attributeNodeName, Field mapField) {\r
+            super(coreNodeType, coreNodeName, attributeNodeType, attributeNodeName);\r
+            this.mapField = mapField;\r
+            this.mapTypeGetter = ParameterGetter.newFieldGetter(this.mapField, null);\r
+\r
+            Class< ? > mapType = this.mapField.getType();\r
+            if (!TypeInfo.isMap(mapType))\r
+                throw new XerialError(XerialErrorCode.InvalidType, String.format(\r
+                        "field: %s is not a Map type", mapField));\r
+\r
+            try {\r
+                this.putter = mapType.getMethod("put", Object.class, Object.class);\r
+            }\r
+            catch (Exception e) {\r
+                throw new XerialError(XerialErrorCode.INVALID_STATE, String.format(\r
+                        "putter method is not found in %s", mapType));\r
+            }\r
+\r
+        }\r
+\r
+        @Override\r
+        public void bind(Object object, Object coreValue, Object attributeValue)\r
+                throws XerialException {\r
+            try {\r
+                Object map = Map.class.cast(mapTypeGetter.get(object));\r
+                if (map == null) {\r
+                    map = TypeInfo.createInstance(mapField.getType());\r
+                    ReflectionUtil.setFieldValue(object, mapField, map);\r
+                }\r
+\r
+                putter.invoke(map, coreValue, attributeValue);\r
+            }\r
+            catch (IllegalArgumentException e) {\r
+                throw new XerialException(XerialErrorCode.WRONG_DATA_TYPE, e);\r
+            }\r
+            catch (IllegalAccessException e) {\r
+                throw new XerialException(XerialErrorCode.INVALID_INPUT, e);\r
+            }\r
+            catch (InvocationTargetException e) {\r
+                throw new XerialError(XerialErrorCode.WRONG_DATA_TYPE, e);\r
+            }\r
+\r
+        }\r
+\r
+    }\r
+\r
 }\r
index c3f283f..f267faf 100644 (file)
@@ -28,6 +28,7 @@ import static org.junit.Assert.*;
 
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.Properties;
 import java.util.TreeMap;
 
@@ -38,22 +39,18 @@ import org.xerial.util.FileResource;
 import org.xerial.util.Pair;
 import org.xerial.util.log.Logger;
 
-public class ObjectLensTest
-{
+public class ObjectLensTest {
 
     private static Logger _logger = Logger.getLogger(ObjectLensTest.class);
 
     @Before
-    public void setUp() throws Exception
-    {}
+    public void setUp() throws Exception {}
 
     @After
-    public void tearDown() throws Exception
-    {}
+    public void tearDown() throws Exception {}
 
     @Test
-    public void pickPairedName() throws Exception
-    {
+    public void pickPairedName() throws Exception {
         Pair<String, String> p = ObjectLens.pickRelationName("Invoice_Order");
         assertEquals("invoice", p.getFirst());
         assertEquals("order", p.getSecond());
@@ -69,8 +66,7 @@ public class ObjectLensTest
     }
 
     @Test
-    public void pickPropertyName() throws Exception
-    {
+    public void pickPropertyName() throws Exception {
         String c = ObjectLens.pickPropertyName("addSomething");
         assertEquals("something", c);
 
@@ -83,8 +79,7 @@ public class ObjectLensTest
     }
 
     @Test
-    public void canonicalNameTest() throws Exception
-    {
+    public void canonicalNameTest() throws Exception {
         assertEquals("itemrgb", ObjectLens.getCanonicalParameterName("itemRgb"));
         assertEquals("itemref", ObjectLens.getCanonicalParameterName("item_ref"));
         assertEquals("helloworld", ObjectLens.getCanonicalParameterName("Hello World"));
@@ -92,8 +87,7 @@ public class ObjectLensTest
 
     }
 
-    public static class ExtMap extends TreeMap<Integer, String>
-    {
+    public static class ExtMap extends TreeMap<Integer, String> {
         /**
          * 
          */
@@ -102,8 +96,7 @@ public class ObjectLensTest
         public String name = "ext-map";
 
         @Override
-        public boolean equals(Object o)
-        {
+        public boolean equals(Object o) {
             if (!(o instanceof ExtMap))
                 return false;
 
@@ -115,8 +108,7 @@ public class ObjectLensTest
     }
 
     @Test
-    public void mapTest() throws Exception
-    {
+    public void mapTest() throws Exception {
         ExtMap extMap = new ExtMap();
         extMap.put(1, "hello");
         extMap.put(10, "world");
@@ -129,8 +121,7 @@ public class ObjectLensTest
         assertEquals(extMap, extMap2);
     }
 
-    public static class ExtList extends ArrayList<Integer>
-    {
+    public static class ExtList extends ArrayList<Integer> {
         /**
          * 
          */
@@ -138,8 +129,7 @@ public class ObjectLensTest
         public String name = "ext-list";
 
         @Override
-        public boolean equals(Object o)
-        {
+        public boolean equals(Object o) {
             if (!(o instanceof ExtList))
                 return false;
 
@@ -151,8 +141,7 @@ public class ObjectLensTest
     }
 
     @Test
-    public void arrayTest() throws Exception
-    {
+    public void arrayTest() throws Exception {
         ExtList extList = new ExtList();
 
         extList.add(10);
@@ -168,24 +157,38 @@ public class ObjectLensTest
 
     }
 
-    public static class PropReader
-    {
+    public static class PropReader {
         Properties prop = new Properties();
 
-        public void put(String key, String value)
-        {
+        public void put(String key, String value) {
             prop.put(key, value);
         }
     }
 
     @Test
-    public void property() throws Exception
-    {
-        PropReader p = Lens.loadSilk(PropReader.class, FileResource.open(ObjectLensTest.class, "property.silk"));
+    public void property() throws Exception {
+        PropReader p = Lens.loadSilk(PropReader.class, FileResource.open(ObjectLensTest.class,
+                "property.silk"));
 
         assertEquals(2, p.prop.size());
         assertEquals("hello", p.prop.get("db.name"));
         assertEquals("sqlite", p.prop.get("db.type"));
     }
 
+    public static class MapField {
+        public Map<Integer, String> id_name;
+    }
+
+    @Test
+    public void mapPutter() throws Exception {
+        MapField m = Lens.loadSilk(MapField.class, FileResource.open(ObjectLensTest.class,
+                "map.silk"));
+        assertNotNull(m.id_name);
+        assertEquals(2, m.id_name.size());
+        String n1 = m.id_name.get(1);
+        String n2 = m.id_name.get(2);
+        assertEquals("leo", n1);
+        assertEquals("yui", n2);
+    }
+
 }
diff --git a/src/test/java/org/xerial/lens/map.silk b/src/test/java/org/xerial/lens/map.silk
new file mode 100644 (file)
index 0000000..b8131b1
--- /dev/null
@@ -0,0 +1,4 @@
+-id:1\r
+ -name: leo\r
+-id:2\r
+ -name: yui\r