You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ftpserver-commits@incubator.apache.org by ng...@apache.org on 2007/02/05 21:08:10 UTC

svn commit: r503870 - in /incubator/ftpserver/trunk/core/src: java/org/apache/ftpserver/util/ClassUtils.java test/org/apache/ftpserver/util/ClassUtilsCastTest.java test/org/apache/ftpserver/util/ClassUtilsTest.java

Author: ngn
Date: Mon Feb  5 13:08:09 2007
New Revision: 503870

URL: http://svn.apache.org/viewvc?view=rev&rev=503870
Log:
Pojo support now handles advanced lists and arrays
Added more tests

Added:
    incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java   (with props)
Modified:
    incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/util/ClassUtils.java
    incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsTest.java

Modified: incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/util/ClassUtils.java
URL: http://svn.apache.org/viewvc/incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/util/ClassUtils.java?view=diff&rev=503870&r1=503869&r2=503870
==============================================================================
--- incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/util/ClassUtils.java (original)
+++ incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/util/ClassUtils.java Mon Feb  5 13:08:09 2007
@@ -33,10 +33,14 @@
 import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.StringTokenizer;
 
 import org.apache.ftpserver.ftplet.Configuration;
 
@@ -77,7 +81,34 @@
         setProperty(target, setter, castValue);
     }
     
+    public static String normalizePropertyName(String propertyName){
+        StringTokenizer st = new StringTokenizer(propertyName, "-");
+        
+        if(st.countTokens() > 1) {
+            StringBuffer sb = new StringBuffer();
+            
+            // add first unchanged
+            sb.append(st.nextToken());
+            
+            while(st.hasMoreTokens()) {
+                String token = st.nextToken().trim();
+                
+                if(token.length() > 0) {
+                    sb.append(Character.toUpperCase(token.charAt(0)));
+                    sb.append(token.substring(1));
+                }
+            }
+            
+            return sb.toString();
+        } else {
+            return propertyName;
+        }
+        
+    }
+    
     private static PropertyDescriptor getDescriptor(Class clazz, String propertyName) {
+        propertyName = normalizePropertyName(propertyName);
+        
         BeanInfo beanInfo;
         try {
             beanInfo = Introspector.getBeanInfo(clazz);
@@ -96,6 +127,115 @@
         return null;
     }
         
+    private static Object createObject(Class clazz, Configuration config, String propValue) {
+        Object value;
+        
+        if(config.isEmpty()) {
+            // regular property
+            value = cast(clazz, propValue);
+        } else {
+            if(clazz == null) {
+                String className = config.getString("class", null);
+                if(className != null) {
+                    try {
+                        clazz = Class.forName(className);
+                    } catch (ClassNotFoundException e) {
+                        throw new RuntimeException("Class not found: " + className, e);
+                    }
+                } else {
+                    // TODO improve error message
+                    throw new RuntimeException("Can not resolve class");
+                }
+            }
+            
+            if(Map.class.isAssignableFrom(clazz)) {
+                Map map = new HashMap();
+                
+                Iterator mapKeys = getKeysInOrder(config.getKeys());
+                
+                while (mapKeys.hasNext()) {
+                    String mapKey = (String) mapKeys.next();
+                    
+                    map.put(mapKey, config.getString(mapKey, null));
+                }
+                
+                value = map;
+            } else if(Collection.class.isAssignableFrom(clazz)) {
+                List list = new ArrayList();
+                
+                Iterator mapKeys = getKeysInOrder(config.getKeys());
+                
+                while (mapKeys.hasNext()) {
+                    String mapKey = (String) mapKeys.next();
+                    
+                    String listValue = config.getString(mapKey, null);
+
+                    list.add(createObject(null, config.subset(mapKey), listValue));
+                }
+                
+                value = list;
+            } else if(clazz.isArray()) {
+                List list = new ArrayList();
+                
+                Iterator mapKeys = getKeysInOrder(config.getKeys());
+                
+                while (mapKeys.hasNext()) {
+                    String mapKey = (String) mapKeys.next();
+                    
+                    String listValue = config.getString(mapKey, null);
+
+                    list.add(createObject(clazz.getComponentType(), config.subset(mapKey), listValue));
+                }
+                
+                Object castArray = Array.newInstance(clazz.getComponentType(), list.size());
+                
+                for (int i = 0; i < list.size(); i++) {
+                    Array.set(castArray, i, list.get(i));
+                } 
+                
+                
+                value = castArray;
+            } else {
+                // create new bean
+                
+                value = createBean(config, clazz.getName());
+            }
+            
+        }
+
+        return value;
+    }
+    
+    public static class KeyComparator implements Comparator {
+        public int compare(Object o1, Object o2) {
+            String key1 = (String) o1;
+            String key2 = (String) o2;
+
+            // assume they are integers
+            try {
+                int intKey1 = Integer.parseInt(key1);
+                int intKey2 = Integer.parseInt(key2);
+            
+                return intKey1 - intKey2;
+            } catch(NumberFormatException e) {
+                return key1.compareToIgnoreCase(key2);
+            }
+        }
+    }
+    
+    private static Iterator getKeysInOrder(Iterator keys) {
+        List keyList = new ArrayList();
+        
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            keyList.add(key);
+        }
+        
+        Collections.sort(keyList, new KeyComparator());
+
+        return keyList.iterator();
+    }
+    
     public static Object createBean(Configuration config, String defaultClass) {
         String className = config.getString("class", defaultClass);
         
@@ -120,32 +260,11 @@
             
             Configuration subConfig = config.subset(key);
             
-            Object value;
+            String propValue = config.getString(key, null);
+            
             PropertyDescriptor descriptor = getDescriptor(clazz, key);
-            if(subConfig.isEmpty()) {
-                // regular property
-                value = cast(descriptor.getPropertyType(), config.getString(key, null));
-            } else {
-                if(Map.class.isAssignableFrom(descriptor.getPropertyType())) {
-                    Map map = new HashMap();
-                    
-                    Iterator mapKeys = subConfig.getKeys();
-                    
-                    while (mapKeys.hasNext()) {
-                        String mapKey = (String) mapKeys.next();
-                        
-                        map.put(mapKey, subConfig.getString(mapKey, null));
-                    }
-                    
-                    value = map;
-                    
-                } else {
-                    // create new bean
-                    
-                    value = createBean(subConfig, descriptor.getPropertyType().getName());
-                }
-                
-            }
+
+            Object value = createObject(descriptor.getPropertyType(), subConfig, propValue);
 
             setProperty(bean, descriptor, value);
         }

Added: incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java
URL: http://svn.apache.org/viewvc/incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java?view=auto&rev=503870
==============================================================================
--- incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java (added)
+++ incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java Mon Feb  5 13:08:09 2007
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */  
+
+package org.apache.ftpserver.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+public class ClassUtilsCastTest extends TestCase {
+    public void testCastToInt() {
+        assertEquals(new Integer(123), ClassUtils.cast(Integer.TYPE, "123"));
+        assertEquals(new Integer(123), ClassUtils.cast(Integer.class, "123"));
+        
+        try {
+            ClassUtils.cast(Integer.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToLong() {
+        assertEquals(new Long(123), ClassUtils.cast(Long.TYPE, "123"));
+        assertEquals(new Long(123), ClassUtils.cast(Long.class, "123"));
+        
+        try {
+            ClassUtils.cast(Long.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToFloat() {
+        assertEquals(new Float(123), ClassUtils.cast(Float.TYPE, "123"));
+        assertEquals(new Float(123), ClassUtils.cast(Float.class, "123"));
+        assertEquals(new Float(1.23), ClassUtils.cast(Float.TYPE, "1.23"));
+        assertEquals(new Float(1.23), ClassUtils.cast(Float.class, "1.23"));
+        
+        try {
+            ClassUtils.cast(Float.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+
+    public void testCastToDouble() {
+        assertEquals(new Double(123), ClassUtils.cast(Double.TYPE, "123"));
+        assertEquals(new Double(123), ClassUtils.cast(Double.class, "123"));
+        assertEquals(new Double(1.23), ClassUtils.cast(Double.TYPE, "1.23"));
+        assertEquals(new Double(1.23), ClassUtils.cast(Double.class, "1.23"));
+        
+        try {
+            ClassUtils.cast(Double.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToByte() {
+        assertEquals(new Byte("3"), ClassUtils.cast(Byte.TYPE, "3"));
+        assertEquals(new Byte("3"), ClassUtils.cast(Byte.class, "3"));
+
+        try {
+            ClassUtils.cast(Byte.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToBigDecimal() {
+        assertEquals(new BigDecimal("1.23"), ClassUtils.cast(BigDecimal.class, "1.23"));
+        
+        try {
+            ClassUtils.cast(BigDecimal.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToBigInteger() {
+        assertEquals(new BigInteger("123"), ClassUtils.cast(BigInteger.class, "123"));
+        
+        try {
+            ClassUtils.cast(BigInteger.class, "foo");
+            fail("Must throw exception");
+        } catch(NumberFormatException e) {
+            // ok
+        }
+    }
+
+    public void testCastToChar() {
+        assertEquals(new Character('a'), ClassUtils.cast(Character.TYPE, "a"));
+        assertEquals(new Character('a'), ClassUtils.cast(Character.class, "a"));
+        
+        try {
+            ClassUtils.cast(Character.class, "foo");
+            fail("Must throw exception");
+        } catch(RuntimeException e) {
+            // ok
+        }
+    }
+
+    public void testCastToBoolean() {
+        assertEquals(Boolean.TRUE, ClassUtils.cast(Boolean.TYPE, "true"));
+        assertEquals(Boolean.TRUE, ClassUtils.cast(Boolean.class, "true"));
+        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.TYPE, "false"));
+        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.class, "false"));
+        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.class, "foo"));
+    }
+    
+    public void testCastToURL() throws Exception {
+        assertEquals(new URL("http://localhost"), ClassUtils.cast(URL.class, "http://localhost"));
+        
+        try {
+            ClassUtils.cast(URL.class, "foo://foo://foo");
+            fail("Must throw exception");
+        } catch(RuntimeException e) {
+            // ok
+        }
+    }
+    
+    public void testCastToInetAddress() throws Exception {
+        assertEquals(InetAddress.getByName("localhost"), ClassUtils.cast(InetAddress.class, "localhost"));
+        assertEquals(InetAddress.getByName("1.2.3.4"), ClassUtils.cast(InetAddress.class, "1.2.3.4"));
+        
+        try {
+            ClassUtils.cast(InetAddress.class, "1.2.3.4.5");
+            fail("Must throw exception");
+        } catch(RuntimeException e) {
+            // ok
+        }
+    }
+}
\ No newline at end of file

Propchange: incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsCastTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsTest.java
URL: http://svn.apache.org/viewvc/incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsTest.java?view=diff&rev=503870&r1=503869&r2=503870
==============================================================================
--- incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsTest.java (original)
+++ incubator/ftpserver/trunk/core/src/test/org/apache/ftpserver/util/ClassUtilsTest.java Mon Feb  5 13:08:09 2007
@@ -19,10 +19,7 @@
 
 package org.apache.ftpserver.util;
 
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.net.InetAddress;
-import java.net.URL;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +32,12 @@
 
 public class ClassUtilsTest extends TestCase {
 
+    public void testNormalizePropertyName() {
+        assertEquals("foo", ClassUtils.normalizePropertyName("foo"));
+        assertEquals("fooBar", ClassUtils.normalizePropertyName("fooBar"));
+        assertEquals("fooBar", ClassUtils.normalizePropertyName("foo-bar"));
+    }
+
     public void testSetProperty() {
         MyBean bean = new MyBean();
         
@@ -48,6 +51,20 @@
 
         assertEquals(123, bean.getBar());
     }
+    
+    public void testSetCamelCasesProperty() {
+        MyBean bean = new MyBean();
+        
+        ClassUtils.setProperty(bean, "camelCasedProp", "flopp");
+        assertEquals("flopp", bean.getCamelCasedProp());
+    }
+
+    public void testSetDashedProperty() {
+        MyBean bean = new MyBean();
+        
+        ClassUtils.setProperty(bean, "camel-cased-prop", "flopp");
+        assertEquals("flopp", bean.getCamelCasedProp());
+    }
 
     public void testSetPropertyWrongCast() {
         MyBean bean = new MyBean();
@@ -138,6 +155,34 @@
         assertEquals("flopp", iter.next());
         assertFalse(iter.hasNext());
     }
+    
+    public void testCreateAdvancedListBean() {
+        Properties props = new Properties();
+        props.setProperty("config.class", MyCollectionBean.class.getName());
+        props.setProperty("config.list.1.class", MyBean.class.getName());
+        props.setProperty("config.list.1.foo", "foo1");
+        props.setProperty("config.list.2.class", MyBean.class.getName());
+        props.setProperty("config.list.2.foo", "foo2");
+        props.setProperty("config.list.3.class", MyBean.class.getName());
+        props.setProperty("config.list.3.foo", "foo3");
+        
+        Configuration config = new PropertiesConfiguration(props);
+        
+        MyCollectionBean bean = (MyCollectionBean) ClassUtils.createBean(config, null);
+        
+        Iterator iter = bean.getList().iterator();
+        
+        MyBean myBean1 = (MyBean) iter.next();
+        assertEquals("foo1", myBean1.getFoo());
+
+        MyBean myBean2 = (MyBean) iter.next();
+        assertEquals("foo2", myBean2.getFoo());
+        
+        MyBean myBean3 = (MyBean) iter.next();
+        assertEquals("foo3", myBean3.getFoo());
+        
+        assertFalse(iter.hasNext());
+    }
 
     public void testCreateArrayBean() {
         Properties props = new Properties();
@@ -157,163 +202,94 @@
         assertEquals(1234, array[3]);
     }
     
-    public void testCreateMapBean() {
+    public void testCreateAdvancedArrayBean() {
         Properties props = new Properties();
-        props.setProperty("config.class", MyMapBean.class.getName());
-        props.setProperty("config.map.foo1", "bar1");
-        props.setProperty("config.map.foo2", "bar2");
-        props.setProperty("config.map.foo3", "bar3");
-        props.setProperty("config.map.foo4", "bar4");
+        props.setProperty("config.class", MyCollectionBean.class.getName());
+        props.setProperty("config.myBeans.1.foo", "foo1");
+        props.setProperty("config.myBeans.2.foo", "foo2");
+        props.setProperty("config.myBeans.3.foo", "foo3");
         
         Configuration config = new PropertiesConfiguration(props);
         
-        MyMapBean bean = (MyMapBean) ClassUtils.createBean(config, null);
+        MyCollectionBean bean = (MyCollectionBean) ClassUtils.createBean(config, null);
         
-        Map map = bean.getMap();
+        MyBean[] array = bean.getMyBeans();
         
-        assertEquals(4, map.size());
-        assertEquals("bar1", map.get("foo1"));
-        assertEquals("bar2", map.get("foo2"));
-        assertEquals("bar3", map.get("foo3"));
-        assertEquals("bar4", map.get("foo4"));
+        assertEquals(3, array.length);
+        assertEquals("foo1", array[0].getFoo());
+        assertEquals("foo2", array[1].getFoo());
+        assertEquals("foo3", array[2].getFoo());
     }
-    
-    
-    /////////////////////////////////
-    // Test cast method
-    public void testCastToInt() {
-        assertEquals(new Integer(123), ClassUtils.cast(Integer.TYPE, "123"));
-        assertEquals(new Integer(123), ClassUtils.cast(Integer.class, "123"));
+
+    public void testCreateSubClassArrayBean() {
+        Properties props = new Properties();
+        props.setProperty("config.class", MyCollectionBean.class.getName());
+        props.setProperty("config.myBeans.1.class", MySubBean.class.getName());
+        props.setProperty("config.myBeans.1.foo", "foo1");
         
-        try {
-            ClassUtils.cast(Integer.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
-    }
-    
-    public void testCastToLong() {
-        assertEquals(new Long(123), ClassUtils.cast(Long.TYPE, "123"));
-        assertEquals(new Long(123), ClassUtils.cast(Long.class, "123"));
+        Configuration config = new PropertiesConfiguration(props);
         
-        try {
-            ClassUtils.cast(Long.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
-    }
-    
-    public void testCastToFloat() {
-        assertEquals(new Float(123), ClassUtils.cast(Float.TYPE, "123"));
-        assertEquals(new Float(123), ClassUtils.cast(Float.class, "123"));
-        assertEquals(new Float(1.23), ClassUtils.cast(Float.TYPE, "1.23"));
-        assertEquals(new Float(1.23), ClassUtils.cast(Float.class, "1.23"));
+        MyCollectionBean bean = (MyCollectionBean) ClassUtils.createBean(config, null);
         
-        try {
-            ClassUtils.cast(Float.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
-    }
-
-    public void testCastToDouble() {
-        assertEquals(new Double(123), ClassUtils.cast(Double.TYPE, "123"));
-        assertEquals(new Double(123), ClassUtils.cast(Double.class, "123"));
-        assertEquals(new Double(1.23), ClassUtils.cast(Double.TYPE, "1.23"));
-        assertEquals(new Double(1.23), ClassUtils.cast(Double.class, "1.23"));
+        MyBean[] array = bean.getMyBeans();
         
-        try {
-            ClassUtils.cast(Double.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
+        assertEquals(1, array.length);
+        assertTrue(array[0] instanceof MySubBean);
+        assertEquals("foo1", array[0].getFoo());
     }
-    
-    public void testCastToByte() {
-        assertEquals(new Byte("3"), ClassUtils.cast(Byte.TYPE, "3"));
-        assertEquals(new Byte("3"), ClassUtils.cast(Byte.class, "3"));
 
-        try {
-            ClassUtils.cast(Byte.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
-    }
-    
-    public void testCastToBigDecimal() {
-        assertEquals(new BigDecimal("1.23"), ClassUtils.cast(BigDecimal.class, "1.23"));
+    public void testCreateLongArrayBean() {
+        Properties props = new Properties();
+        props.setProperty("config.class", MyCollectionBean.class.getName());
         
-        try {
-            ClassUtils.cast(BigDecimal.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
+        for(int i = 1; i<13; i++) {
+            props.setProperty("config.array." + i, Integer.toString(i));
         }
-    }
-    
-    public void testCastToBigInteger() {
-        assertEquals(new BigInteger("123"), ClassUtils.cast(BigInteger.class, "123"));
         
-        try {
-            ClassUtils.cast(BigInteger.class, "foo");
-            fail("Must throw exception");
-        } catch(NumberFormatException e) {
-            // ok
-        }
-    }
-
-    public void testCastToChar() {
-        assertEquals(new Character('a'), ClassUtils.cast(Character.TYPE, "a"));
-        assertEquals(new Character('a'), ClassUtils.cast(Character.class, "a"));
+        Configuration config = new PropertiesConfiguration(props);
         
-        try {
-            ClassUtils.cast(Character.class, "foo");
-            fail("Must throw exception");
-        } catch(RuntimeException e) {
-            // ok
-        }
-    }
-
-    public void testCastToBoolean() {
-        assertEquals(Boolean.TRUE, ClassUtils.cast(Boolean.TYPE, "true"));
-        assertEquals(Boolean.TRUE, ClassUtils.cast(Boolean.class, "true"));
-        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.TYPE, "false"));
-        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.class, "false"));
-        assertEquals(Boolean.FALSE, ClassUtils.cast(Boolean.class, "foo"));
-    }
-    
-    public void testCastToURL() throws Exception {
-        assertEquals(new URL("http://localhost"), ClassUtils.cast(URL.class, "http://localhost"));
+        MyCollectionBean bean = (MyCollectionBean) ClassUtils.createBean(config, null);
         
-        try {
-            ClassUtils.cast(URL.class, "foo://foo://foo");
-            fail("Must throw exception");
-        } catch(RuntimeException e) {
-            // ok
+        int[] array = bean.getArray();
+        
+        assertEquals(12, array.length);
+        for(int i = 0; i<12; i++) {
+            assertEquals(i+1, array[i]);
         }
     }
     
-    public void testCastToInetAddress() throws Exception {
-        assertEquals(InetAddress.getByName("localhost"), ClassUtils.cast(InetAddress.class, "localhost"));
-        assertEquals(InetAddress.getByName("1.2.3.4"), ClassUtils.cast(InetAddress.class, "1.2.3.4"));
+    public void testCreateMapBean() {
+        Properties props = new Properties();
+        props.setProperty("config.class", MyMapBean.class.getName());
+        props.setProperty("config.map.foo1", "bar1");
+        props.setProperty("config.map.foo2", "bar2");
+        props.setProperty("config.map.foo3", "bar3");
+        props.setProperty("config.map.foo4", "bar4");
         
-        try {
-            ClassUtils.cast(InetAddress.class, "1.2.3.4.5");
-            fail("Must throw exception");
-        } catch(RuntimeException e) {
-            // ok
-        }
+        Configuration config = new PropertiesConfiguration(props);
+        
+        MyMapBean bean = (MyMapBean) ClassUtils.createBean(config, null);
+        
+        Map map = bean.getMap();
+        
+        assertEquals(4, map.size());
+        assertEquals("bar1", map.get("foo1"));
+        assertEquals("bar2", map.get("foo2"));
+        assertEquals("bar3", map.get("foo3"));
+        assertEquals("bar4", map.get("foo4"));
     }
-
+ 
     public static class MyCollectionBean {
         private List list;
         private int[] array;
+        private MyBean[] myBeans;
 
+        public MyBean[] getMyBeans() {
+            return myBeans;
+        }
+        public void setMyBeans(MyBean[] myBeans) {
+            this.myBeans = myBeans;
+        }
         public int[] getArray() {
             return array;
         }
@@ -344,6 +320,7 @@
     public static class MyBean {
         private String foo;
         private int bar;
+        private String camelCasedProp;
         
         public int getBar() {
             return bar;
@@ -357,7 +334,15 @@
         public void setFoo(String foo) {
             this.foo = foo;
         }
+        public String getCamelCasedProp() {
+            return camelCasedProp;
+        }
+        public void setCamelCasedProp(String camelCasedProp) {
+            this.camelCasedProp = camelCasedProp;
+        }
     }
+    
+    public static class MySubBean extends MyBean {}
     
     public static class MyOtherBean {
         private MyBean myBean;