You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2009/08/22 16:36:30 UTC

svn commit: r806861 - in /commons/proper/configuration/branches/configuration2_experimental/src: main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java

Author: oheger
Date: Sat Aug 22 14:36:30 2009
New Revision: 806861

URL: http://svn.apache.org/viewvc?rev=806861&view=rev
Log:
Initial implementation of MapConfigurationSource.

Added:
    commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java   (with props)
    commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java   (with props)

Added: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java?rev=806861&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java Sat Aug 22 14:36:30 2009
@@ -0,0 +1,283 @@
+/*
+ * 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.commons.configuration2.base;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * An implementation of the {@code ConfigurationSource} interface that holds its
+ * data in a map.
+ * </p>
+ * <p>
+ * Using a map as store for configuration settings is a natural choice as the
+ * methods defined by the {@code ConfigurationSource} interface can be easily
+ * implemented on top of a map. So most methods more or less directly delegate
+ * to the map. At construction time either a map can be provided which will then
+ * be used as store or a new one is created.
+ * </p>
+ * <p>
+ * One extension to to a plain map is the feature that each key can be
+ * associated with multiple values. If a property key is added that already
+ * exists, a list is created containing all property values.
+ * </p>
+ * <p>
+ * This class can also be used as a base class for other configuration sources
+ * that a map-like structure and keep their whole data in memory. An example
+ * could be a configuration source wrapping a properties file.
+ * </p>
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public class MapConfigurationSource implements ConfigurationSource
+{
+    /** The map acting as data store. */
+    private final Map<String, Object> store;
+
+    /**
+     * Creates a new instance of {@code MapConfigurationSource} and initializes
+     * it with the given map. The map is then used as store for the
+     * configuration settings managed by this object. After passing the map to
+     * this constructor it should not be accessed any more from other code. (As
+     * the map may become large, we do not want to make a defensive copy.)
+     *
+     * @param map the map serving as data store for this {@code
+     *        MapConfigurationSource} (must not be <b>null</b>)
+     * @throws IllegalArgumentException if the map is <b>null</b>
+     */
+    public MapConfigurationSource(Map<String, Object> map)
+    {
+        if (map == null)
+        {
+            throw new IllegalArgumentException("Map must not be null!");
+        }
+
+        store = map;
+    }
+
+    /**
+     * Creates a new, empty instance of {@code MapConfigurationSource}. A new
+     * map is created that is used as internal data store.
+     */
+    public MapConfigurationSource()
+    {
+        this(new LinkedHashMap<String, Object>());
+    }
+
+    /**
+     * Adds the specified {@code ConfigurationSourceListener} at this object.
+     * This class does not support event listeners, so an exception is thrown.
+     *
+     * @param l the event listener to be added
+     * @throws UnsupportedOperationException as this operation is not supported
+     */
+    public void addConfigurationSourceListener(ConfigurationSourceListener l)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Adds the given property to this {@code ConfigurationSource}. If the
+     * property does not exist yet, it is newly added. Otherwise only the new
+     * value is added to the existing values of this property.
+     *
+     * @param key the key of the property
+     * @param value the value to be added
+     */
+    public void addProperty(String key, Object value)
+    {
+        Object previousValue = getProperty(key);
+
+        if (previousValue == null)
+        {
+            getStore().put(key, value);
+        }
+        else if (previousValue instanceof List<?>)
+        {
+            // the value is added to the existing list
+            @SuppressWarnings("unchecked")
+            List<Object> currentValues = (List<Object>) previousValue;
+            currentValues.add(value);
+        }
+        else
+        {
+            // the previous value is replaced by a list containing the previous
+            // value and the new value
+            List<Object> list = new ArrayList<Object>();
+            list.add(previousValue);
+            list.add(value);
+
+            getStore().put(key, list);
+        }
+    }
+
+    /**
+     * Clears all data from this {@code ConfigurationSource}. This
+     * implementation clears the internal map used as data storage.
+     */
+    public void clear()
+    {
+        getStore().clear();
+    }
+
+    /**
+     * Removes the specified property. This implementation removes the given key
+     * from the internal map used as data storage.
+     *
+     * @param key the key of the property to be removed
+     */
+    public void clearProperty(String key)
+    {
+        getStore().remove(key);
+    }
+
+    /**
+     * Checks whether this {@code ConfigurationSource} contains a property with
+     * the given key. This implementation checks the internal data map.
+     *
+     * @param key the key of the property in question
+     * @return a flag whether a property with this key exists
+     */
+    public boolean containsKey(String key)
+    {
+        return getStore().containsKey(key);
+    }
+
+    /**
+     * Returns an iterator for the keys of the properties contained in this
+     * {@code ConfigurationSource}. This iterator is obtained from the internal
+     * map.
+     *
+     * @return an iterator with the keys of all properties
+     */
+    public Iterator<String> getKeys()
+    {
+        return getStore().keySet().iterator();
+    }
+
+    /**
+     * Returns an iterator for all property keys starting with the specified
+     * prefix. This operation is not supported, so always an exception is
+     * thrown.
+     *
+     * @param prefix the desired prefix
+     * @return an iterator with all the keys starting with this prefix
+     * @throws UnsupportedOperationException as this operation is not
+     *         implemented
+     */
+    public Iterator<String> getKeys(String prefix)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Returns the value of the property with the given key. This implementation
+     * directly accesses the internal map. If the property has multiple values,
+     * the result is a {@code List} object with all values. If the property is
+     * unknown, <b>null</b> is returned.
+     *
+     * @param key the key of the property
+     * @return the value of the property
+     */
+    public Object getProperty(String key)
+    {
+        return getStore().get(key);
+    }
+
+    /**
+     * Returns a flag whether this {@code ConfigurationSource} is empty. This
+     * implementation checks the internal data map whether it is empty.
+     *
+     * @return <b>true</b> if this {@code ConfigurationSource} is empty,
+     *         <b>false</b> otherwise
+     */
+    public boolean isEmpty()
+    {
+        return getStore().isEmpty();
+    }
+
+    /**
+     * Removes the specified {@code ConfigurationSourceListener} from this
+     * object. This class does not support event listeners, so an exception is
+     * thrown.
+     *
+     * @param l the listener to be removed
+     * @return a flag whether the listener could be removed successfully
+     * @throws UnsupportedOperationException as this operation is not supported
+     */
+    public boolean removeConfigurationSourceListener(
+            ConfigurationSourceListener l)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Sets the value of the specified property. If the property does not exist
+     * yet, this method has the same effect as
+     * {@link #addProperty(String, Object)}. Otherwise the value of this
+     * property is replaced by the new one.
+     *
+     * @param key the key of the property
+     * @param value the new value of this property
+     */
+    public void setProperty(String key, Object value)
+    {
+        getStore().put(key, value);
+    }
+
+    /**
+     * Returns the size of this {@code ConfigurationSource}. This implementation
+     * returns the size of the internal data map.
+     *
+     * @return the size of this {@code ConfigurationSource}
+     */
+    public int size()
+    {
+        return getStore().size();
+    }
+
+    /**
+     * Returns the number of values stored for the property with the given name.
+     * This operation is not supported, so this implementation always throws an
+     * exception.
+     *
+     * @param key the key of the property in question
+     * @return the number of values stored for this property
+     * @throws UnsupportedOperationException as this operation is not supported
+     */
+    public int valueCount(String key)
+    {
+        throw new UnsupportedOperationException("Not implemented!");
+    }
+
+    /**
+     * Returns the underlying map, in which the data of this {@code
+     * MapConfigurationSource} is stored. This method is intended to be used by
+     * derived classes that need direct access to this store.
+     *
+     * @return the map with the data
+     */
+    protected Map<String, Object> getStore()
+    {
+        return store;
+    }
+}

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/MapConfigurationSource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java?rev=806861&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java Sat Aug 22 14:36:30 2009
@@ -0,0 +1,364 @@
+/*
+ * 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.commons.configuration2.base;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+
+/**
+ * Test class for {@code MapConfigurationSource}.
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public class TestMapConfigurationSource extends TestCase
+{
+    /** Constant for a test key. */
+    private static final String KEY = "testKey";
+
+    /** Constant for the number of test keys. */
+    private static final int COUNT = 8;
+
+    /**
+     * Helper method for checking a list property. Tests whether the property
+     * value is a list that contains exactly the given elements.
+     *
+     * @param value the value
+     * @param expected the expected values
+     */
+    private static void checkList(Object value, Object... expected)
+    {
+        assertTrue("Not a list: " + value, value instanceof List<?>);
+        List<?> lst = (List<?>) value;
+        assertEquals("Wrong size", expected.length, lst.size());
+        for (int i = 0; i < expected.length; i++)
+        {
+            assertEquals("Wrong value at " + i, expected[i], lst.get(i));
+        }
+    }
+
+    /**
+     * Creates a map with test data. The map contains {@link #COUNT} keys
+     * starting with the prefix {@link #KEY} and a running index. Their values
+     * are their indices.
+     *
+     * @return the map with test data
+     */
+    private static Map<String, Object> setUpMap()
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        for (int i = 0; i < COUNT; i++)
+        {
+            map.put(KEY + i, i);
+        }
+        return map;
+    }
+
+    /**
+     * Tries to create an instance without a map. This should cause an
+     * exception.
+     */
+    public void testInitNoMap()
+    {
+        try
+        {
+            new MapConfigurationSource(null);
+            fail("Could create instance without a map!");
+        }
+        catch (IllegalArgumentException iex)
+        {
+            // ok
+        }
+    }
+
+    /**
+     * Tests whether an instance can be created from an existing map.
+     */
+    public void testInitMap()
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        for (int i = 0; i < COUNT; i++)
+        {
+            map.put(KEY + i, i);
+        }
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        assertSame("Wrong map as store", map, src.getStore());
+        for (int i = 0; i < COUNT; i++)
+        {
+            assertEquals("Wrong property value", Integer.valueOf(i), src
+                    .getProperty(KEY + i));
+        }
+    }
+
+    /**
+     * Tests the default constructor.
+     */
+    public void testInitDefault()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        Map<String, Object> store = src.getStore();
+        assertTrue("Store not empty", src.isEmpty());
+        assertTrue("Wrong map class: " + store,
+                store instanceof LinkedHashMap<?, ?>);
+    }
+
+    /**
+     * Tests adding a single property value.
+     */
+    public void testAddPropertySingleValue()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        final Integer value = 1;
+        src.addProperty(KEY, value);
+        assertEquals("Value not in store", value, src.getStore().get(KEY));
+        assertEquals("Wrong property value", value, src.getProperty(KEY));
+    }
+
+    /**
+     * Tests whether multiple values for a property can be added.
+     */
+    public void testAddPropertyMultipleValues()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        src.addProperty(KEY, 1);
+        src.addProperty(KEY, 2);
+        checkList(src.getStore().get(KEY), 1, 2);
+        src.addProperty(KEY, 3);
+        checkList(src.getStore().get(KEY), 1, 2, 3);
+    }
+
+    /**
+     * Tests whether the whole source can be cleared.
+     */
+    public void testClear()
+    {
+        Map<String, Object> map = setUpMap();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        src.clear();
+        assertTrue("Store not cleared", map.isEmpty());
+    }
+
+    /**
+     * Tests whether a property can be removed.
+     */
+    public void testClearProperty()
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put(KEY, 42);
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        src.clearProperty(KEY);
+        assertFalse("Property not removed", map.containsKey(KEY));
+    }
+
+    /**
+     * Tests the containsKey() implementation.
+     */
+    public void testContainsKey()
+    {
+        Map<String, Object> map = setUpMap();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        for (String k : map.keySet())
+        {
+            assertTrue("Key not found: " + k, src.containsKey(k));
+        }
+    }
+
+    /**
+     * Tests containsKey() for a non-existing key.
+     */
+    public void testContainsKeyNonExisting()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        assertFalse("Found non-existing key", src.containsKey(KEY));
+    }
+
+    /**
+     * Tests whether all keys of the source can be retrieved.
+     */
+    public void testGetKeys()
+    {
+        Map<String, Object> map = setUpMap();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        Set<String> keys = new HashSet<String>(map.keySet());
+        for (Iterator<String> it = src.getKeys(); it.hasNext();)
+        {
+            String k = it.next();
+            assertTrue("Unexpected key: " + k, keys.remove(k));
+        }
+        assertTrue("Remaining keys: " + keys, keys.isEmpty());
+    }
+
+    /**
+     * Tests whether the order of keys is retained.
+     */
+    public void testGetKeysOrdered()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        for (int i = 0; i < COUNT; i++)
+        {
+            src.addProperty(KEY + i, i);
+        }
+        Iterator<String> it = src.getKeys();
+        for (int i = 0; i < COUNT; i++)
+        {
+            assertEquals("Wrong key at " + i, KEY + i, it.next());
+        }
+        assertFalse("Too many keys", it.hasNext());
+    }
+
+    /**
+     * Tests the getKeys() method that expects a prefix. This method is not
+     * implemented.
+     */
+    public void testGetKeysPrefix()
+    {
+        MapConfigurationSource src = new MapConfigurationSource(setUpMap());
+        try
+        {
+            src.getKeys(KEY);
+            fail("Could obtain keys with prefix!");
+        }
+        catch (UnsupportedOperationException uoex)
+        {
+            // ok
+        }
+    }
+
+    /**
+     * Tests getProperty() for a non-existing property.
+     */
+    public void testGetPropertyNonExisting()
+    {
+        MapConfigurationSource src = new MapConfigurationSource();
+        assertNull("Got value for non-existing property", src.getProperty(KEY));
+    }
+
+    /**
+     * Tests the isEmpty() implementation.
+     */
+    public void testIsEmpty()
+    {
+        Map<String, Object> map = setUpMap();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        assertFalse("Source is empty", src.isEmpty());
+        map.clear();
+        assertTrue("Source not empty", src.isEmpty());
+    }
+
+    /**
+     * Tests setProperty() if the property does not exist yet.
+     */
+    public void testSetPropertyNew()
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        final Object value = "a test value";
+        src.setProperty(KEY, value);
+        assertEquals("Property not set", value, map.get(KEY));
+        assertEquals("Too many values set", 1, map.size());
+    }
+
+    /**
+     * Tests whether an existing property can be overridden.
+     */
+    public void testSetPropertyOverride()
+    {
+        Map<String, Object> map = setUpMap();
+        MapConfigurationSource src = new MapConfigurationSource(map);
+        final String key = KEY + "1";
+        final Object value = "the new value";
+        src.setProperty(key, value);
+        assertEquals("Value not changed", value, map.get(key));
+        assertEquals("Number of properties changed", COUNT, map.size());
+    }
+
+    /**
+     * Tests whether the size of the source can be queried.
+     */
+    public void testSize()
+    {
+        MapConfigurationSource src = new MapConfigurationSource(setUpMap());
+        assertEquals("Wrong size", COUNT, src.size());
+    }
+
+    /**
+     * Tests the valueCount() implementation. This method is not supported, so
+     * an exception should be thrown.
+     */
+    public void testValueCount()
+    {
+        MapConfigurationSource src = new MapConfigurationSource(setUpMap());
+        try
+        {
+            src.valueCount(KEY);
+            fail("Could invoke valueCount()!");
+        }
+        catch (UnsupportedOperationException uex)
+        {
+            // ok
+        }
+    }
+
+    /**
+     * Tries to add an event listener. This is not supported, so an exception
+     * should be thrown.
+     */
+    public void testAddConfigurationSourceListener()
+    {
+        ConfigurationSourceListener l = EasyMock
+                .createNiceMock(ConfigurationSourceListener.class);
+        MapConfigurationSource src = new MapConfigurationSource();
+        try
+        {
+            src.addConfigurationSourceListener(l);
+            fail("Could add an event listener!");
+        }
+        catch (UnsupportedOperationException uex)
+        {
+            // ok
+        }
+    }
+
+    /**
+     * Tries to remove an event listener. Event listeners are not supported, so
+     * this should cause an exception.
+     */
+    public void testRemoveConfigurationSourceListener()
+    {
+        ConfigurationSourceListener l = EasyMock
+                .createNiceMock(ConfigurationSourceListener.class);
+        MapConfigurationSource src = new MapConfigurationSource();
+        try
+        {
+            src.removeConfigurationSourceListener(l);
+            fail("Could remove an event listener!");
+        }
+        catch (UnsupportedOperationException uex)
+        {
+            // ok
+        }
+    }
+}

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestMapConfigurationSource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain