- a null or empty key can now be used to retrieve the subset root element.
- subset.getKeys() and subset.getKeys(prefix) are now fixed, previously they returned the keys of the parent configuration and not the keys of the subset.
- AbstractConfiguration.getKeys(prefix) has been changed to use a FilterIterator and fix Bug 27427.
Emmanuel Bourg
/* * Copyright 2004 The Apache Software Foundation. * * Licensed 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.configuration; import org.apache.commons.collections.iterators.TransformIterator; import org.apache.commons.collections.Transformer; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Properties; /** * A subset of another configuration. The new Configuration object contains * every key from the parent Configuration that starts with prefix. The prefix * is removed from the keys in the subset. * * @author Emmanuel Bourg * @version $Revision$, $Date$ */ public class SubsetConfiguration implements Configuration { protected Configuration parent; protected String prefix; protected String delimiter; /** * Create a subset of the specified configuration * * @param parent The parent configuration * @param prefix The prefix used to select the properties. */ public SubsetConfiguration(Configuration parent, String prefix) { this.parent = parent; this.prefix = prefix; } /** * Create a subset of the specified configuration * * @param parent The parent configuration * @param prefix The prefix used to select the properties. * @param delimiter The prefix delimiter */ public SubsetConfiguration(Configuration parent, String prefix, String delimiter) { this.parent = parent; this.prefix = prefix; this.delimiter = delimiter; } /** * Return the key in the parent configuration associated to the specified * key in this subset. * * @param key The key in the subset. */ protected String getParentKey(String key) { if ("".equals(key) || key == null) { return prefix; } else { return delimiter == null ? prefix + key : prefix + delimiter + key; } } /** * Return the key in the subset configuration associated to the specified * key in the parent configuration. * * @param key The key in the parent configuration. */ protected String getChildKey(String key) { if (!key.startsWith(prefix)) { throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset."); } else { String modifiedKey = null; if (key.length() == prefix.length()) { modifiedKey = ""; } else { int i = prefix.length() + (delimiter != null ? delimiter.length() : 0); modifiedKey = key.substring(i); } return modifiedKey; } } /** * Return the parent configuation for this subset. */ public Configuration getParent() { return parent; } /** * Return the prefix used to select the properties in the parent configuration. */ public String getPrefix() { return prefix; } /** * Set the prefix used to select the properties in the parent configuration. */ public void setPrefix(String prefix) { this.prefix = prefix; } public Configuration subset(String prefix) { return parent.subset(getParentKey(prefix)); } public boolean isEmpty() { return !getKeys().hasNext(); } public boolean containsKey(String key) { return parent.containsKey(getParentKey(key)); } public void addProperty(String key, Object value) { parent.addProperty(getParentKey(key), value); } public void setProperty(String key, Object value) { parent.setProperty(getParentKey(key), value); } public void clearProperty(String key) { parent.clearProperty(getParentKey(key)); } public Object getProperty(String key) { return parent.getProperty(getParentKey(key)); } public Iterator getKeys(String prefix) { return new TransformIterator(parent.getKeys(getParentKey(prefix)), new Transformer() { public Object transform(Object obj) { return getChildKey((String) obj); } }); } public Iterator getKeys() { return new TransformIterator(parent.getKeys(prefix), new Transformer() { public Object transform(Object obj) { return getChildKey((String) obj); } }); } public Properties getProperties(String key) { return parent.getProperties(getParentKey(key)); } public boolean getBoolean(String key) { return parent.getBoolean(getParentKey(key)); } public boolean getBoolean(String key, boolean defaultValue) { return parent.getBoolean(getParentKey(key), defaultValue); } public Boolean getBoolean(String key, Boolean defaultValue) { return parent.getBoolean(getParentKey(key), defaultValue); } public byte getByte(String key) { return parent.getByte(getParentKey(key)); } public byte getByte(String key, byte defaultValue) { return parent.getByte(getParentKey(key), defaultValue); } public Byte getByte(String key, Byte defaultValue) { return parent.getByte(getParentKey(key), defaultValue); } public double getDouble(String key) { return parent.getDouble(getParentKey(key)); } public double getDouble(String key, double defaultValue) { return parent.getDouble(getParentKey(key), defaultValue); } public Double getDouble(String key, Double defaultValue) { return parent.getDouble(getParentKey(key), defaultValue); } public float getFloat(String key) { return parent.getFloat(getParentKey(key)); } public float getFloat(String key, float defaultValue) { return parent.getFloat(getParentKey(key), defaultValue); } public Float getFloat(String key, Float defaultValue) { return parent.getFloat(getParentKey(key), defaultValue); } public int getInt(String key) { return parent.getInt(getParentKey(key)); } public int getInt(String key, int defaultValue) { return parent.getInt(getParentKey(key), defaultValue); } public Integer getInteger(String key, Integer defaultValue) { return parent.getInteger(getParentKey(key), defaultValue); } public long getLong(String key) { return parent.getLong(getParentKey(key)); } public long getLong(String key, long defaultValue) { return parent.getLong(getParentKey(key), defaultValue); } public Long getLong(String key, Long defaultValue) { return parent.getLong(getParentKey(key), defaultValue); } public short getShort(String key) { return parent.getShort(getParentKey(key)); } public short getShort(String key, short defaultValue) { return parent.getShort(getParentKey(key), defaultValue); } public Short getShort(String key, Short defaultValue) { return parent.getShort(getParentKey(key), defaultValue); } public BigDecimal getBigDecimal(String key) { return parent.getBigDecimal(getParentKey(key)); } public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) { return parent.getBigDecimal(getParentKey(key), defaultValue); } public BigInteger getBigInteger(String key) { return parent.getBigInteger(getParentKey(key)); } public BigInteger getBigInteger(String key, BigInteger defaultValue) { return parent.getBigInteger(getParentKey(key), defaultValue); } public String getString(String key) { return parent.getString(getParentKey(key)); } public String getString(String key, String defaultValue) { return parent.getString(getParentKey(key), defaultValue); } public String[] getStringArray(String key) { return parent.getStringArray(getParentKey(key)); } public List getList(String key) { return parent.getList(getParentKey(key)); } public List getList(String key, List defaultValue) { return parent.getList(getParentKey(key), defaultValue); } public Locale getLocale(String key) { return parent.getLocale(getParentKey(key)); } public Locale getLocale(String key, Locale defaultValue) { return parent.getLocale(getParentKey(key), defaultValue); } }
/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed 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.configuration; import junit.framework.TestCase; import java.util.Iterator; /** * Test case for the [EMAIL PROTECTED] SubsetConfiguration} class. * * @author Emmanuel Bourg * @version $Revision$, $Date$ */ public class TestSubsetConfiguration extends TestCase { public void testGetProperty() { BaseConfiguration conf = new BaseConfiguration(); conf.setProperty("test.key1", "value1"); conf.setProperty("testing.key2", "value1"); Configuration subset = new SubsetConfiguration(conf, "test", "."); assertFalse("the subset is empty", subset.isEmpty()); assertTrue("'key1' not found in the subset", subset.containsKey("key1")); assertFalse("'ng.key2' found in the subset", subset.containsKey("ng.key2")); } public void testSetProperty() { BaseConfiguration conf = new BaseConfiguration(); Configuration subset = new SubsetConfiguration(conf, "test", "."); // set a property in the subset and check the parent subset.setProperty("key1", "value1"); assertEquals("key1 in the subset configuration", "value1", subset.getProperty("key1")); assertEquals("test.key1 in the parent configuration", "value1", conf.getProperty("test.key1")); // set a property in the parent and check in the subset conf.setProperty("test.key2", "value2"); assertEquals("test.key2 in the parent configuration", "value2", conf.getProperty("test.key2")); assertEquals("key2 in the subset configuration", "value2", subset.getProperty("key2")); } public void testGetParentKey() { // subset with delimiter SubsetConfiguration subset = new SubsetConfiguration(null, "prefix", "."); assertEquals("parent key for \"key\"", "prefix.key", subset.getParentKey("key")); assertEquals("parent key for \"\"", "prefix", subset.getParentKey("")); // subset without delimiter subset = new SubsetConfiguration(null, "prefix", null); assertEquals("parent key for \"key\"", "prefixkey", subset.getParentKey("key")); assertEquals("parent key for \"\"", "prefix", subset.getParentKey("")); } public void testGetChildKey() { // subset with delimiter SubsetConfiguration subset = new SubsetConfiguration(null, "prefix", "."); assertEquals("parent key for \"prefixkey\"", "key", subset.getChildKey("prefix.key")); assertEquals("parent key for \"prefix\"", "", subset.getChildKey("prefix")); // subset without delimiter subset = new SubsetConfiguration(null, "prefix", null); assertEquals("parent key for \"prefixkey\"", "key", subset.getChildKey("prefixkey")); assertEquals("parent key for \"prefix\"", "", subset.getChildKey("prefix")); } public void testGetKeys() { BaseConfiguration conf = new BaseConfiguration(); conf.setProperty("test", "value0"); conf.setProperty("test.key1", "value1"); conf.setProperty("testing.key2", "value1"); Configuration subset = new SubsetConfiguration(conf, "test", "."); Iterator it = subset.getKeys(); assertEquals("1st key", "", it.next()); assertEquals("2nd key", "key1", it.next()); assertFalse("too many elements", it.hasNext()); } public void testGetKeysWithPrefix() { BaseConfiguration conf = new BaseConfiguration(); conf.setProperty("test.abc", "value0"); conf.setProperty("test.abc.key1", "value1"); conf.setProperty("test.abcdef.key2", "value1"); Configuration subset = new SubsetConfiguration(conf, "test", "."); Iterator it = subset.getKeys("abc"); assertEquals("1st key", "abc", it.next()); assertEquals("2nd key", "abc.key1", it.next()); assertFalse("too many elements", it.hasNext()); } }
Index: src/java/org/apache/commons/configuration/AbstractConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/java/org/apache/commons/configuration/AbstractConfiguration.java,v retrieving revision 1.6 diff -u -r1.6 AbstractConfiguration.java --- src/java/org/apache/commons/configuration/AbstractConfiguration.java 27 Feb 2004 17:41:35 -0000 1.6 +++ src/java/org/apache/commons/configuration/AbstractConfiguration.java 5 Mar 2004 16:58:32 -0000 @@ -16,17 +16,20 @@ * limitations under the License. */ +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.NoSuchElementException; import java.util.Properties; import java.util.StringTokenizer; -import java.math.BigDecimal; -import java.math.BigInteger; import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.iterators.FilterIterator; /** * Abstract configuration class. Provide basic functionality but does not @@ -270,63 +273,7 @@ */ public Configuration subset(String prefix) { - BaseConfiguration c = new BaseConfiguration(); - Iterator keys = this.getKeys(); - boolean validSubset = false; - - while (keys.hasNext()) - { - Object key = keys.next(); - - if (key instanceof String && ((String) key).startsWith(prefix)) - { - if (!validSubset) - { - validSubset = true; - } - - String newKey = null; - - /* - * Check to make sure that c.subset(prefix) doesn't blow up when - * there is only a single property with the key prefix. This is - * not a useful subset but it is a valid subset. - */ - if (((String) key).length() == prefix.length()) - { - newKey = prefix; - } - else - { - newKey = ((String) key).substring(prefix.length() + 1); - } - - /* - * use addPropertyDirect() - this will plug the data as is into - * the Map, but will also do the right thing re key accounting - * - * QUESTION: getProperty or getPropertyDirect - */ - Object value = getProperty((String) key); - if (value instanceof String) - { - c.addPropertyDirect(newKey, interpolate((String) value)); - } - else - { - c.addProperty(newKey, value); - } - } - } - - if (validSubset) - { - return c; - } - else - { - return null; - } + return new SubsetConfiguration(this, prefix, "."); } /** @@ -384,21 +331,20 @@ * * @return An Iterator of keys that match the prefix. */ - public Iterator getKeys(String prefix) - { - Iterator keys = getKeys(); - ArrayList matchingKeys = new ArrayList(); + public Iterator getKeys(final String prefix) { - while (keys.hasNext()) - { - Object key = keys.next(); + return new FilterIterator(getKeys(), new Predicate() { + public boolean evaluate(Object obj) { + boolean matching = false; + + if (obj instanceof String) { + String key = (String) obj; + matching = key.startsWith(prefix + ".") || key.equals(prefix); + } - if (key instanceof String && ((String) key).startsWith(prefix)) - { - matchingKeys.add(key); + return matching; } - } - return matchingKeys.iterator(); + }); } /** Index: src/java/org/apache/commons/configuration/CompositeConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/java/org/apache/commons/configuration/CompositeConfiguration.java,v retrieving revision 1.7 diff -u -r1.7 CompositeConfiguration.java --- src/java/org/apache/commons/configuration/CompositeConfiguration.java 27 Feb 2004 17:41:35 -0000 1.7 +++ src/java/org/apache/commons/configuration/CompositeConfiguration.java 5 Mar 2004 16:58:32 -0000 @@ -247,28 +247,7 @@ } return false; } - /** - * Create a CompositeConfiguration object that is a subset - * of this one. Cycles over all the config objects, and calls - * their subset method and then just adds that. - * - * @param prefix - */ - public Configuration subset(String prefix) - { - CompositeConfiguration subsetCompositeConfiguration = - new CompositeConfiguration(); - for (ListIterator i = configList.listIterator(); i.hasNext();) - { - Configuration config = (Configuration) i.next(); - Configuration subset = config.subset(prefix); - if (subset != null) - { - subsetCompositeConfiguration.addConfiguration(subset); - } - } - return subsetCompositeConfiguration; - } + /** * Get a List of strings associated with the given configuration key. * Index: src/java/org/apache/commons/configuration/HierarchicalConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java,v retrieving revision 1.3 diff -u -r1.3 HierarchicalConfiguration.java --- src/java/org/apache/commons/configuration/HierarchicalConfiguration.java 27 Feb 2004 17:41:35 -0000 1.3 +++ src/java/org/apache/commons/configuration/HierarchicalConfiguration.java 5 Mar 2004 16:58:33 -0000 @@ -333,49 +333,6 @@ } /** - * Creates a new <code>Configuration</code> object containing all keys - * that start with the specified prefix. This implementation will return - * a <code>HierarchicalConfiguration</code> object so that the structure - * of the keys will be saved. - * @param prefix the prefix of the keys for the subset - * @return a new configuration object representing the selected subset - */ - public Configuration subset(String prefix) - { - Collection nodes = fetchNodeList(prefix); - if (nodes.isEmpty()) - { - return null; - } /* if */ - - HierarchicalConfiguration result = new HierarchicalConfiguration(); - CloneVisitor visitor = new CloneVisitor(); - - for (Iterator it = nodes.iterator(); it.hasNext();) - { - Node nd = (Node) it.next(); - nd.visit(visitor, null); - - Container children = visitor.getClone().getChildren(); - if (children.size() > 0) - { - for (int i = 0; i < children.size(); i++) - { - result.getRoot().addChild((Node) children.get(i)); - } /* for */ - } /* if */ - else - { - // In this case we cannot shorten the key because only - // values are found without further child nodes. - result.getRoot().addChild(visitor.getClone()); - } /* else */ - } /* for */ - - return (result.isEmpty()) ? null : result; - } - - /** * Returns the maximum defined index for the given key. This is * useful if there are multiple values for this key. They can then be * addressed separately by specifying indices from 0 to the return value Index: src/java/org/apache/commons/configuration/JNDIConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/java/org/apache/commons/configuration/JNDIConfiguration.java,v retrieving revision 1.7 diff -u -r1.7 JNDIConfiguration.java --- src/java/org/apache/commons/configuration/JNDIConfiguration.java 27 Feb 2004 17:41:35 -0000 1.7 +++ src/java/org/apache/commons/configuration/JNDIConfiguration.java 5 Mar 2004 16:58:33 -0000 @@ -285,65 +285,6 @@ return false; } } - /** - * Create an ExtendedProperties object that is a subset - * of this one. Take into account duplicate keys - * by using the setProperty() in ExtendedProperties. - * - * @param prefix - */ - public Configuration subset(String prefix) - { - BaseConfiguration c = new BaseConfiguration(); - Iterator keys = this.getKeys(); - boolean validSubset = false; - while (keys.hasNext()) - { - Object key = keys.next(); - if (key instanceof String && ((String) key).startsWith(prefix)) - { - if (!validSubset) - { - validSubset = true; - } - String newKey = null; - /* - * Check to make sure that c.subset(prefix) doesn't blow up when - * there is only a single property with the key prefix. This is - * not a useful subset but it is a valid subset. - */ - if (((String) key).length() == prefix.length()) - { - newKey = prefix; - } - else - { - newKey = ((String) key).substring(prefix.length() + 1); - } - /* - * use addPropertyDirect() - this will plug the data as is into - * the Map, but will also do the right thing re key accounting - */ - Object value = getValueFromJNDI(key.toString()); - if (value instanceof String) - { - c.addPropertyDirect(newKey, interpolate((String) value)); - } - else - { - c.addPropertyDirect(newKey, value); - } - } - } - if (validSubset) - { - return (Configuration) c; - } - else - { - return null; - } - } /** * Get a boolean associated with the given configuration key. Index: src/test/org/apache/commons/configuration/NonStringTestHolder.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/test/org/apache/commons/configuration/NonStringTestHolder.java,v retrieving revision 1.4 diff -u -r1.4 NonStringTestHolder.java --- src/test/org/apache/commons/configuration/NonStringTestHolder.java 27 Feb 2004 17:41:34 -0000 1.4 +++ src/test/org/apache/commons/configuration/NonStringTestHolder.java 5 Mar 2004 17:06:52 -0000 @@ -130,39 +130,30 @@ public void testListMissing() throws Exception { - - Assert.assertEquals( - 0, - configuration.getList("missing.list").size()); + Assert.assertEquals(0, configuration.getList("missing.list").size()); } public void testSubset() throws Exception { - String KEY_VALUE = "test.short"; - Configuration subset = configuration.subset(KEY_VALUE); - boolean foundKeyValue = false; - for (Iterator i = subset.getKeys(); i.hasNext();) - { - String key = (String) i.next(); - if (!key.equals(KEY_VALUE)) - { + Configuration subset = configuration.subset("test"); - Assert.assertTrue( - "Key is:" + key, - !key.startsWith("test.short")); - } - else { - foundKeyValue=true; - } + // search the "short" key in the subset using the key iterator + boolean foundKeyValue = false; + Iterator it = subset.getKeys(); + while (it.hasNext() && !foundKeyValue) + { + String key = (String) it.next(); + foundKeyValue = "short".equals(key); } - Assert.assertTrue("Make sure test.short did show up. It is valid.",foundKeyValue); + + Assert.assertTrue("'short' key not found in the subset key iterator", foundKeyValue); } public void testIsEmpty() throws Exception { - Assert.assertTrue("Configuration should not be empty",!configuration.isEmpty()); - + Assert.assertTrue("Configuration should not be empty", !configuration.isEmpty()); } + /** * @return */ Index: src/test/org/apache/commons/configuration/TestCompositeConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/test/org/apache/commons/configuration/TestCompositeConfiguration.java,v retrieving revision 1.5 diff -u -r1.5 TestCompositeConfiguration.java --- src/test/org/apache/commons/configuration/TestCompositeConfiguration.java 27 Feb 2004 17:41:34 -0000 1.5 +++ src/test/org/apache/commons/configuration/TestCompositeConfiguration.java 5 Mar 2004 16:58:34 -0000 @@ -210,7 +210,7 @@ } /** - * Tests retrieving subsets of configuraitions + * Tests retrieving subsets of configurations */ public void testGettingSubset() throws Exception { @@ -218,14 +218,14 @@ cc.addConfiguration(dom4jConf); Configuration subset = null; - subset = cc.subset("test.short"); + subset = cc.subset("test"); assertNotNull(subset); - assertTrue("Shouldn't be empty", !subset.isEmpty()); - assertEquals("Make sure the initial loaded configs subset overrides" + "any later add configs subset", "1", subset.getString("test.short")); + assertFalse("Shouldn't be empty", subset.isEmpty()); + assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "1", subset.getString("short")); cc.setProperty("test.short", "43"); - subset = cc.subset("test.short"); - assertEquals("Make sure the initial loaded configs subset overrides" + "any later add configs subset", "43", subset.getString("test.short")); + subset = cc.subset("test"); + assertEquals("Make sure the initial loaded configs subset overrides any later add configs subset", "43", subset.getString("short")); } /** Index: src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/configuration/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java,v retrieving revision 1.3 diff -u -r1.3 TestHierarchicalConfiguration.java --- src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java 27 Feb 2004 17:41:34 -0000 1.3 +++ src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java 5 Mar 2004 16:58:34 -0000 @@ -235,12 +235,12 @@ key.append("name"); assertEquals(fields[0][i], conf.getProperty(key.toString())); } /* for */ - - assertNull(config.subset("tables.table(2)")); - - conf = config.subset("tables.table.fields.field.name"); + + assertTrue("subset is not empty", config.subset("tables.table(2)").isEmpty()); + + conf = config.subset("tables.table.fields.field"); prop = conf.getProperty("name"); - assertTrue(prop instanceof Collection); + assertTrue("prop is not a collection", prop instanceof Collection); assertEquals(10, ((Collection) prop).size()); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]