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