Anonymous avatar Anonymous committed 81e49dc

hierarchies _actually_ work now -- with tests!

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@238 e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (7)

src/java/com/opensymphony/xwork/util/InstantiatingNullHandler.java

  */
 package com.opensymphony.xwork.util;
 
-import com.opensymphony.util.FileManager;
 import com.opensymphony.xwork.ObjectFactory;
 import ognl.NullHandler;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.util.*;
 
  * Normally does nothing, but if {@link #CREATE_NULL_OBJECTS} is in the action context
  * with a value of true, then this class will attempt to create null objects when Ognl
  * requests null objects be created.
- *
+ * <p/>
  * The following rules are used:
  * <ul>
- *  <li>If the null property is a simple bean with a no-arg constructor, it will simply be
- *      created using ObjectFactory's {@link ObjectFactory#buildBean(java.lang.Class) buildBean} method.</li>
- *  <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then this class
- *      will look in the conversion property file (see {@link XWorkConverter}) for an entry
- *      with a key of the form "Collection_[propertyName]". Using the value of this key as
- *      the class type in which the collection will be holding, an {@link XWorkList} will be
- *      created, allowing simple dynamic insertion.</li>
- *  <li>If the property is declared as a {@link Map}, then the same rules are applied for
- *      list, except that an {@link XWorkMap} will be created instead.</li>
+ * <li>If the null property is a simple bean with a no-arg constructor, it will simply be
+ * created using ObjectFactory's {@link ObjectFactory#buildBean(java.lang.Class) buildBean} method.</li>
+ * <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then this class
+ * will look in the conversion property file (see {@link XWorkConverter}) for an entry
+ * with a key of the form "Collection_[propertyName]". Using the value of this key as
+ * the class type in which the collection will be holding, an {@link XWorkList} will be
+ * created, allowing simple dynamic insertion.</li>
+ * <li>If the property is declared as a {@link Map}, then the same rules are applied for
+ * list, except that an {@link XWorkMap} will be created instead.</li>
  * </ul>
  *
  * @author Matt Ho
         try {
             Class clazz = method.getParameterTypes()[0];
             Object param = createObject(context, clazz, target, property.toString());
-            method.invoke(target, new Object[] {param});
+            method.invoke(target, new Object[]{param});
 
             return param;
         } catch (Exception e) {
         return null;
     }
 
-    private Class getCollectionType(Class clazz, String property) {
-        Class propClass = null;
-
-        if (!noMapping.contains(clazz)) {
-            try {
-                Map mapping = (Map) mappings.get(clazz);
-
-                if (mapping == null) {
-                    mapping = buildConverterMapping(clazz);
-                } else {
-                    mapping = conditionalReload(clazz, mapping);
-                }
-
-                propClass = (Class) mapping.get("Collection_" + property);
-            } catch (Throwable t) {
-                noMapping.add(clazz);
-            }
-        }
-
-        return propClass;
+    protected Class getCollectionType(Class clazz, String property) {
+        return (Class) XWorkConverter.getInstance().getConverter(clazz, "Collection_" + property);
     }
 
     /**
      * Method into the methodMap keyed by property name
      *
      * @param propertyName the name of the property we're looking up
-     * @param instance of instance of the Class we're attempting to find the setter for
+     * @param instance     of instance of the Class we're attempting to find the setter for
      */
     private Method getMethod(Map methodMap, String propertyName, Object instance) {
         synchronized (methodMap) {
         }
     }
 
-    private Map buildConverterMapping(Class clazz) throws Exception {
-        Map mapping = new HashMap();
-
-        String resource = XWorkConverter.buildConverterFilename(clazz);
-        InputStream is = FileManager.loadFile(resource, clazz);
-
-        if (is != null) {
-            Properties props = new Properties();
-            props.load(is);
-            mapping.putAll(props);
-
-            for (Iterator iterator = mapping.entrySet().iterator();
-                    iterator.hasNext();) {
-                Map.Entry entry = (Map.Entry) iterator.next();
-                String propName = (String) entry.getKey();
-                String className = (String) entry.getValue();
-
-                if (propName.startsWith("Collection_")) {
-                    entry.setValue(Class.forName(className));
-                }
-            }
-
-            mappings.put(clazz, mapping);
-        } else {
-            noMapping.add(clazz);
-        }
-
-        return mapping;
-    }
-
-    private Map conditionalReload(Class clazz, Map oldValues) throws Exception {
-        Map mapping = oldValues;
-
-        if (FileManager.isReloadingConfigs()) {
-            if (FileManager.fileNeedsReloading(XWorkConverter.buildConverterFilename(clazz))) {
-                mapping = buildConverterMapping(clazz);
-            }
-        }
-
-        return mapping;
-    }
-
     private Object createObject(Map context, Class clazz, Object target, String property) throws Exception {
         if ((clazz == Collection.class) || (clazz == List.class)) {
             Class collectionType = getCollectionType(target.getClass(), property);

src/java/com/opensymphony/xwork/util/XWorkConverter.java

 package com.opensymphony.xwork.util;
 
 import com.opensymphony.util.FileManager;
-
 import com.opensymphony.xwork.ActionContext;
 import com.opensymphony.xwork.ObjectFactory;
-
-import ognl.*;
-
+import ognl.DefaultTypeConverter;
+import ognl.Evaluation;
+import ognl.OgnlContext;
+import ognl.TypeConverter;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import java.io.InputStream;
-
 import java.lang.reflect.Member;
-
 import java.util.*;
 
 
                 property = (String) classProp[1];
             }
 
-            if (!noMapping.contains(clazz)) {
-                try {
-                    Map mapping = (Map) mappings.get(clazz);
-
-                    if (mapping == null) {
-                        mapping = buildConverterMapping(clazz);
-                    } else {
-                        mapping = conditionalReload(clazz, mapping);
-                    }
-
-                    tc = (TypeConverter) mapping.get(property);
-                } catch (Throwable t) {
-                    noMapping.add(clazz);
-                }
-            }
+            tc = (TypeConverter) getConverter(clazz, property);
         }
 
         if (tc == null) {
         }
     }
 
+    protected Object getConverter(Class clazz, String property) {
+        if (!noMapping.contains(clazz)) {
+            try {
+                Map mapping = (Map) mappings.get(clazz);
+
+                if (mapping == null) {
+                    mapping = buildConverterMapping(clazz);
+                } else {
+                    mapping = conditionalReload(clazz, mapping);
+                }
+
+                return mapping.get(property);
+            } catch (Throwable t) {
+                noMapping.add(clazz);
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Looks for a TypeConverter in the default mappings.
      *
      * @param className name of the class the TypeConverter must handle
      * @return a TypeConverter to handle the specified class or null if none can
-     * be found
+     *         be found
      */
     public TypeConverter lookup(String className) {
         if (unknownMappings.contains(className)) {
      *
      * @param clazz the class the TypeConverter must handle
      * @return a TypeConverter to handle the specified class or null if none can
-     * be found
+     *         be found
      */
     public TypeConverter lookup(Class clazz) {
         return lookup(clazz.getName());
      * on a key that already exists, the converter is ignored.
      *
      * @param mapping an existing map to add new converter mappings to
-     * @param clazz class to look for converter mappings for
+     * @param clazz   class to look for converter mappings for
      */
     private void addConverterMapping(Map mapping, Class clazz) {
         try {
                     if (!key.startsWith("Collection_")) {
                         mapping.put(key, createTypeConverter((String) entry.getValue()));
                     } else {
-                        mapping.put(key, entry.getValue());
+                        mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue()));
                     }
                 }
             }
 
         while (!curClazz.equals(Object.class)) {
             // add current class' mappings
-            addConverterMapping(mapping, clazz);
+            addConverterMapping(mapping, curClazz);
 
             // check interfaces' mappings
             Class[] interfaces = curClazz.getInterfaces();
         props.load(is);
 
         for (Iterator iterator = props.entrySet().iterator();
-                iterator.hasNext();) {
+             iterator.hasNext();) {
             Map.Entry entry = (Map.Entry) iterator.next();
             String key = (String) entry.getKey();
 
      *
      * @param clazz the class the TypeConverter must handle
      * @return a TypeConverter to handle the specified class or null if none can
-     * be found
+     *         be found
      */
     private TypeConverter lookupSuper(Class clazz) {
         TypeConverter result = null;

src/test/com/opensymphony/xwork/util/Cat-conversion.properties

+Collection_kittens = com.opensymphony.xwork.util.Cat

src/test/com/opensymphony/xwork/util/Cat.java

  */
 package com.opensymphony.xwork.util;
 
+import java.util.List;
+
 
 /**
- *
- *
  * @author <a href="mailto:plightbo@cisco.com">Pat Lightbody</a>
  * @author $Author$
  * @version $Revision$
 
     Foo foo;
     String name;
+    List kittens;
 
     //~ Methods ////////////////////////////////////////////////////////////////
 
     public String getName() {
         return name;
     }
+
+    public List getKittens() {
+        return kittens;
+    }
+
+    public void setKittens(List kittens) {
+        this.kittens = kittens;
+    }
 }

src/test/com/opensymphony/xwork/util/InstantiatingNullHandlerTest.java

+package com.opensymphony.xwork.util;
+
+import junit.framework.TestCase;
+
+public class InstantiatingNullHandlerTest extends TestCase {
+    public void testInheritance() {
+        Tiger t = new Tiger();
+        InstantiatingNullHandler nh = new InstantiatingNullHandler();
+
+        Class clazz = nh.getCollectionType(Tiger.class, "dogs");
+        assertEquals(Dog.class, clazz);
+
+        clazz = nh.getCollectionType(Tiger.class, "kittens");
+        assertEquals(Cat.class, clazz);
+    }
+}

src/test/com/opensymphony/xwork/util/Tiger-conversion.properties

+Collection_dogs = com.opensymphony.xwork.util.Dog

src/test/com/opensymphony/xwork/util/Tiger.java

+package com.opensymphony.xwork.util;
+
+import java.util.List;
+
+public class Tiger extends Cat {
+    List dogs;
+
+    public List getDogs() {
+        return dogs;
+    }
+
+    public void setDogs(List dogs) {
+        this.dogs = dogs;
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.