Author: oheger
Date: Mon Aug 19 14:33:13 2013
New Revision: 1515446

URL: http://svn.apache.org/r1515446
Log:
Implemented the new generic get methods in AbstractConfiguration.

AbstractConfiguration now has a property of type ConversionHandler. The
implementations of the new methods delegate to this converion handler object.

Modified:
    
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
    
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java

Modified: 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java?rev=1515446&r1=1515445&r2=1515446&view=diff
==============================================================================
--- 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
 (original)
+++ 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
 Mon Aug 19 14:33:13 2013
@@ -32,6 +32,8 @@ import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.commons.configuration.convert.ConversionException;
+import org.apache.commons.configuration.convert.ConversionHandler;
+import org.apache.commons.configuration.convert.DefaultConversionHandler;
 import org.apache.commons.configuration.convert.DefaultListDelimiterHandler;
 import org.apache.commons.configuration.convert.DisabledListDelimiterHandler;
 import org.apache.commons.configuration.convert.PropertyConverter;
@@ -138,9 +140,16 @@ public abstract class AbstractConfigurat
     /** end token */
     protected static final String END_TOKEN = "}";
 
+    /** The default {@code ConversionHandler} instance. */
+    private static final ConversionHandler DEF_CONVERSION_HANDLER =
+            new DefaultConversionHandler();
+
     /** The list delimiter handler. */
     private ListDelimiterHandler listDelimiterHandler;
 
+    /** The conversion handler. */
+    private ConversionHandler conversionHandler;
+
     /**
      * Whether the configuration should throw NoSuchElementExceptions or simply
      * return null when a property does not exist. Defaults to return null.
@@ -165,6 +174,7 @@ public abstract class AbstractConfigurat
         setLogger(null);
         installDefaultInterpolator();
         listDelimiterHandler = DisabledListDelimiterHandler.INSTANCE;
+        conversionHandler = DEF_CONVERSION_HANDLER;
     }
 
     /**
@@ -204,6 +214,42 @@ public abstract class AbstractConfigurat
     }
 
     /**
+     * Returns the {@code ConversionHandler} used by this instance.
+     *
+     * @return the {@code ConversionHandler}
+     * @since 2.0
+     */
+    public ConversionHandler getConversionHandler()
+    {
+        return conversionHandler;
+    }
+
+    /**
+     * Sets the {@code ConversionHandler} to be used by this instance. The
+     * {@code ConversionHandler} is responsible for every kind of data type
+     * conversion. It is consulted by all get methods returning results in
+     * specific data types. A newly created configuration uses a default
+     * {@code ConversionHandler} implementation. This can be changed while
+     * initializing the configuration (e.g. via a builder). Note that access to
+     * this property is not synchronized.
+     *
+     * @param conversionHandler the {@code ConversionHandler} to be used (must
+     *        not be <b>null</b>)
+     * @throws IllegalArgumentException if the {@code ConversionHandler} is
+     *         <b>null</b>
+     * @since 2.0
+     */
+    public void setConversionHandler(ConversionHandler conversionHandler)
+    {
+        if (conversionHandler == null)
+        {
+            throw new IllegalArgumentException(
+                    "ConversionHandler must not be null!");
+        }
+        this.conversionHandler = conversionHandler;
+    }
+
+    /**
      * Allows to set the {@code throwExceptionOnMissing} flag. This
      * flag controls the behavior of property getter methods that return
      * objects if the requested property is missing. If the flag is set to
@@ -1600,52 +1646,85 @@ public abstract class AbstractConfigurat
 
     public <T> T get(Class<T> cls, String key)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        return get(cls, key, null);
     }
 
+    /**
+     * {@inheritDoc} This implementation delegates to the
+     * {@link ConversionHandler} to perform the actual type conversion.
+     */
     public <T> T get(Class<T> cls, String key, T defaultValue)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        return ObjectUtils.defaultIfNull(
+                getConversionHandler().to(getProperty(key), cls,
+                        getInterpolator()), defaultValue);
     }
 
     public Object getArray(Class<?> cls, String key)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        return getArray(cls, key, null);
     }
 
+    /**
+     * {@inheritDoc} This implementation delegates to the
+     * {@link ConversionHandler} to perform the actual type conversion. If this
+     * results in a <b>null</b> result (because the property is undefined), the
+     * default value is returned. It is checked whether the default value is an
+     * array with the correct component type. If not, an exception is thrown.
+     *
+     * @throws IllegalArgumentException if the default value is not a 
compatible
+     *         array
+     */
     public Object getArray(Class<?> cls, String key, Object defaultValue)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        checkDefaultValueArray(cls, defaultValue);
+        return ObjectUtils.defaultIfNull(
+                getConversionHandler().toArray(getProperty(key), cls,
+                        getInterpolator()), defaultValue);
     }
 
     public <T> List<T> getList(Class<T> cls, String key)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        return getList(cls, key, null);
     }
 
+    /**
+     * {@inheritDoc} This implementation delegates to the generic
+     * {@code getCollection()}. As target collection a newly created
+     * {@code ArrayList} is passed in.
+     */
     public <T> List<T> getList(Class<T> cls, String key, List<T> defaultValue)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        List<T> result = new ArrayList<T>();
+        getCollection(cls, key, result, defaultValue);
+        return result;
     }
 
     public <T> Collection<T> getCollection(Class<T> cls, String key,
             Collection<T> target)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        return getCollection(cls, key, target, null);
     }
 
+    /**
+     * {@inheritDoc} This implementation delegates to the
+     * {@link ConversionHandler} to perform the actual conversion. If no target
+     * collection is provided, an {@code ArrayList} is created.
+     */
     public <T> Collection<T> getCollection(Class<T> cls, String key,
             Collection<T> target, Collection<T> defaultValue)
     {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Not yet implemented!");
+        Object src = getProperty(key);
+        if (src == null)
+        {
+            return handleDefaultCollection(target, defaultValue);
+        }
+
+        Collection<T> targetCol =
+                (target != null) ? target : new ArrayList<T>();
+        getConversionHandler().toCollection(src, cls, getInterpolator(),
+                targetCol);
+        return targetCol;
     }
 
     /**
@@ -1820,4 +1899,61 @@ public abstract class AbstractConfigurat
         }
         return result;
     }
+
+    /**
+     * Checks an object provided as default value for the {@code getArray()}
+     * method. Throws an exception if this is not an array with the correct
+     * component type.
+     *
+     * @param cls the component class for the array
+     * @param defaultValue the default value object to be checked
+     * @throws IllegalArgumentException if this is not a valid default object
+     */
+    private static void checkDefaultValueArray(Class<?> cls, Object 
defaultValue)
+    {
+        if (defaultValue != null
+                && (!defaultValue.getClass().isArray() || !cls
+                        .isAssignableFrom(defaultValue.getClass()
+                                .getComponentType())))
+        {
+            throw new IllegalArgumentException(
+                    "The type of the default value (" + defaultValue.getClass()
+                            + ")" + " is not an array of the specified class ("
+                            + cls + ")");
+        }
+    }
+
+    /**
+     * Handles the default collection for a collection conversion. This method
+     * fills the target collection with the content of the default collection.
+     * Both collections may be <b>null</b>.
+     *
+     * @param target the target collection
+     * @param defaultValue the default collection
+     * @return the initialized target collection
+     */
+    private static <T> Collection<T> handleDefaultCollection(Collection<T> 
target,
+            Collection<T> defaultValue)
+    {
+        Collection<T> defCol;
+        if (defaultValue != null)
+        {
+            defCol = defaultValue;
+        }
+        else
+        {
+            defCol = Collections.emptyList();
+        }
+        Collection<T> result;
+        if (target == null)
+        {
+            result = new ArrayList<T>(defCol);
+        }
+        else
+        {
+            target.addAll(defCol);
+            result = target;
+        }
+        return result;
+    }
 }

Modified: 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java?rev=1515446&r1=1515445&r2=1515446&view=diff
==============================================================================
--- 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
 (original)
+++ 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
 Mon Aug 19 14:33:13 2013
@@ -16,7 +16,9 @@
  */
 package org.apache.commons.configuration;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -30,6 +32,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.configuration.convert.ConversionHandler;
+import org.apache.commons.configuration.convert.DefaultConversionHandler;
 import org.apache.commons.configuration.convert.DefaultListDelimiterHandler;
 import org.apache.commons.configuration.convert.DisabledListDelimiterHandler;
 import org.apache.commons.configuration.event.ConfigurationEvent;
@@ -631,6 +635,286 @@ public class TestAbstractConfigurationBa
     }
 
     /**
+     * Tests whether a configuration instance has a default conversion hander.
+     */
+    @Test
+    public void testDefaultConversionHandler()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        assertEquals("Wrong default conversion handler",
+                DefaultConversionHandler.class, config.getConversionHandler()
+                        .getClass());
+    }
+
+    /**
+     * Tests that the default conversion handler is shared between all
+     * configuration instances.
+     */
+    @Test
+    public void testDefaultConversionHandlerSharedInstance()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        PropertiesConfiguration config2 = new PropertiesConfiguration();
+        assertSame("Multiple conversion handlers",
+                config.getConversionHandler(), config2.getConversionHandler());
+    }
+
+    /**
+     * Tests whether the conversion handler can be changed.
+     */
+    @Test
+    public void testSetDefaultConversionHandler()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        ConversionHandler handler = new DefaultConversionHandler();
+        config.setConversionHandler(handler);
+        assertSame("Handler not set", handler, config.getConversionHandler());
+    }
+
+    /**
+     * Tries to set a null value for the conversion handler.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetDefaultConversionHandlerNull()
+    {
+        new PropertiesConfiguration().setConversionHandler(null);
+    }
+
+    /**
+     * Tests the generic get() method.
+     */
+    @Test
+    public void testGet()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        Integer value = 20130816;
+        config.addProperty(KEY_PREFIX, value.toString());
+        assertEquals("Wrong result", value, config.get(Integer.class, 
KEY_PREFIX));
+    }
+
+    /**
+     * Tests get() for an unknown property if no default value is provided.
+     */
+    @Test
+    public void testGetUnknownNoDefaultValue()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        assertNull("Wrong result", config.get(Integer.class, KEY_PREFIX));
+    }
+
+    /**
+     * Tests get() for an unknown property if a default value is provided.
+     */
+    @Test
+    public void testGetUnknownWithDefaultValue()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        Integer defaultValue = 2121;
+        assertEquals("Wrong result", defaultValue,
+                config.get(Integer.class, KEY_PREFIX, defaultValue));
+    }
+
+    /**
+     * Tests whether conversion to an array is possible.
+     */
+    @Test
+    public void testGetArray()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        Integer[] expected = new Integer[PROP_COUNT];
+        for (int i = 0; i < PROP_COUNT; i++)
+        {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected[i] = Integer.valueOf(i);
+        }
+        Integer[] result =
+                (Integer[]) config.getArray(Integer.class, KEY_PREFIX);
+        assertArrayEquals("Wrong result", expected, result);
+    }
+
+    /**
+     * Tests a conversion to an array of primitive types.
+     */
+    @Test
+    public void testGetArrayPrimitive()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        short[] expected = new short[PROP_COUNT];
+        for (int i = 0; i < PROP_COUNT; i++)
+        {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected[i] = (short) i;
+        }
+        short[] result =
+                (short[]) config.getArray(Short.TYPE, KEY_PREFIX, new 
short[0]);
+        assertArrayEquals("Wrong result", expected, result);
+    }
+
+    /**
+     * Tests getArray() for an unknown property if no default value is 
provided.
+     */
+    @Test
+    public void testGetArrayUnknownNoDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        assertNull("Wrong result", config.getArray(Integer.class, KEY_PREFIX));
+    }
+
+    /**
+     * Tests getArray() for an unknown property if a default value is provided.
+     */
+    @Test
+    public void testGetArrayUnknownWithDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        int[] defValue = {
+                1, 2, 3
+        };
+        assertArrayEquals("Wrong result", defValue,
+                (int[]) config.getArray(Integer.TYPE, KEY_PREFIX, defValue));
+    }
+
+    /**
+     * Tests getArray() if the default value is not an array.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetArrayDefaultValueNotAnArray()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getArray(Integer.class, KEY_PREFIX, this);
+    }
+
+    /**
+     * Tests getArray() if the default value is an array with a different
+     * component type.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetArrayDefaultValueWrongComponentClass()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        config.getArray(Integer.class, KEY_PREFIX, new short[1]);
+    }
+
+    /**
+     * Prepares a test configuration for a test for a list conversion. The
+     * configuration is populated with a list property. The returned list
+     * contains the expected list values converted to integers.
+     *
+     * @param config the test configuration
+     * @return the list with expected values
+     */
+    private static List<Integer> prepareListTest(PropertiesConfiguration 
config)
+    {
+        List<Integer> expected = new ArrayList<Integer>(PROP_COUNT);
+        for (int i = 0; i < PROP_COUNT; i++)
+        {
+            config.addProperty(KEY_PREFIX, String.valueOf(i));
+            expected.add(i);
+        }
+        return expected;
+    }
+
+    /**
+     * Tests a conversion to a list.
+     */
+    @Test
+    public void testGetList()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> expected = prepareListTest(config);
+        List<Integer> result = config.getList(Integer.class, KEY_PREFIX);
+        assertEquals("Wrong result", expected, result);
+    }
+
+    /**
+     * Tests a conversion to a list if the property is unknown and no default
+     * value is provided.
+     */
+    @Test
+    public void testGetListUnknownNoDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        assertTrue("Wrong result", config.getList(Integer.class, 
KEY_PREFIX).isEmpty());
+    }
+
+    /**
+     * Tests a conversion to a list if the property is unknown and a default
+     * list is provided.
+     */
+    @Test
+    public void testGetListUnknownWithDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> defValue = Arrays.asList(1, 2, 3);
+        assertEquals("Wrong result", defValue, config.getList(Integer.class, 
KEY_PREFIX, defValue));
+    }
+
+    /**
+     * Tests a conversion to a collection.
+     */
+    @Test
+    public void testGetCollection()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> expected = prepareListTest(config);
+        List<Integer> result = new ArrayList<Integer>(PROP_COUNT);
+        assertSame("Wrong result", result, config.getCollection(Integer.class, 
KEY_PREFIX, result));
+        assertEquals("Wrong converted content", expected, result);
+    }
+
+    /**
+     * Tests getCollection() if no target collection is provided.
+     */
+    @Test
+    public void testGetCollectionNullTarget()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> expected = prepareListTest(config);
+        Collection<Integer> result = config.getCollection(Integer.class, 
KEY_PREFIX, null, new ArrayList<Integer>());
+        assertEquals("Wrong result", expected, result);
+    }
+
+    /**
+     * Tests whether a single value property can be converted to a collection.
+     */
+    @Test
+    public void testGetCollectionSingleValue()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        config.addProperty(KEY_PREFIX, "1");
+        List<Integer> result = new ArrayList<Integer>(1);
+        config.getCollection(Integer.class, KEY_PREFIX, result);
+        assertEquals("Wrong number of elements", 1, result.size());
+        assertEquals("Wrong element", Integer.valueOf(1), result.get(0));
+    }
+
+    /**
+     * Tests getCollection() for an unknown property if no default value is
+     * provided.
+     */
+    @Test
+    public void testGetCollectionUnknownNoDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> result = new ArrayList<Integer>();
+        assertSame("Wrong result", result, config.getCollection(Integer.class, 
KEY_PREFIX, result));
+        assertTrue("Got elements", result.isEmpty());
+    }
+
+    /**
+     * Tests getCollection() for an unknown property if a default collection is
+     * provided.
+     */
+    @Test
+    public void testGetCollectionUnknownWithDefault()
+    {
+        PropertiesConfiguration config = new PropertiesConfiguration();
+        List<Integer> defValue = Arrays.asList(1, 2, 4, 8, 16, 32);
+        Collection<Integer> result = config.getCollection(Integer.class, 
KEY_PREFIX, null, defValue);
+        assertEquals("Wrong result", defValue, result);
+    }
+
+    /**
      * Creates the source configuration for testing the copy() and append()
      * methods. This configuration contains keys with an odd index and values
      * starting with the prefix "src". There are also some list properties.


Reply via email to