You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by oh...@apache.org on 2006/01/21 17:54:42 UTC
svn commit: r371084 - in /jakarta/commons/proper/configuration/trunk/src:
java/org/apache/commons/configuration/SubnodeConfiguration.java
test/org/apache/commons/configuration/TestSubnodeConfiguration.java
Author: oheger
Date: Sat Jan 21 08:54:24 2006
New Revision: 371084
URL: http://svn.apache.org/viewcvs?rev=371084&view=rev
Log:
Added initial implementation of SubnodeConfiguration
Added:
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java (with props)
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java (with props)
Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java?rev=371084&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java Sat Jan 21 08:54:24 2006
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2006 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.configuration.tree.ExpressionEngine;
+
+/**
+ * <p>
+ * A specialized hierarchical configuration class that wraps a single node of
+ * its parent configuration.
+ * </p>
+ * <p>
+ * Configurations of this type are initialized with a parent configuration and a
+ * configuration node of this configuration. This node becomes the root node of
+ * the subnode configuration. All property accessor methods are evaluated
+ * relative to this root node. A good use case for a
+ * <code>SubnodeConfiguration</code> is when multiple properties from a
+ * specific sub tree of the whole configuration need to be accessed. Then a
+ * <code>SubnodeConfiguration</code> can be created with the parent node of
+ * the affected sub tree as root node. This allows for simpler property keys and
+ * is also more efficient.
+ * </p>
+ * <p>
+ * A subnode configuration and its parent configuration operate on the same
+ * hierarchy of configuration nodes. So if modifications are performed at the
+ * subnode configuration, these changes are immideately visible in the parent
+ * configuration. Analogously will updates of the parent configuration affect
+ * the subnode configuration if the sub tree spanned by the subnode
+ * configuration's root node is involved.
+ * </p>
+ * <p>
+ * A subnode configuration and its parent configuration work very close
+ * together. For instance, some flags used by configuration objects (e.g. the
+ * <code>throwExceptionOnMissing</code> flag or the settings for handling list
+ * delimiters) are shared between both. This means if one of these properties is
+ * modified for the subnode configuration, it is also changed for the parent and
+ * vice versa.
+ * </p>
+ * <p>
+ * From its purpose this class is quite similar to
+ * <code>{@link SubsetConfiguration}</code>. The difference is that a subset
+ * configuration of a hierarchical configuration may combine multiple
+ * configuration nodes from different sub trees of the configuration, while all
+ * nodes in a subnode configuration belong to the same sub tree. If an
+ * application can live with this limitation, it is recommended to use this
+ * class instead of <code>SubsetConfiguration</code> because creating a subset
+ * configuration is more expensive than creating a subnode configuration.
+ * </p>
+ *
+ * @since 1.3
+ * @author Oliver Heger
+ * @version $Id$
+ */
+public class SubnodeConfiguration extends HierarchicalConfiguration
+{
+ /** Stores the parent configuration. */
+ private HierarchicalConfiguration parent;
+
+ /**
+ * Creates a new instance of <code>SubnodeConfiguration</code> and
+ * initializes it with the parent configuration and the new root node.
+ *
+ * @param parent the parent configuration
+ * @param root the root node of this subnode configuration
+ */
+ public SubnodeConfiguration(HierarchicalConfiguration parent, Node root)
+ {
+ if (parent == null)
+ {
+ throw new IllegalArgumentException(
+ "Parent configuration must not be null!");
+ }
+ if (root == null)
+ {
+ throw new IllegalArgumentException("Root node must not be null!");
+ }
+
+ setRoot(root);
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the parent configuration of this subnode configuration.
+ *
+ * @return the parent configuration
+ */
+ public HierarchicalConfiguration getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Returns the expression engine. This method returns the parent's
+ * expression engine.
+ *
+ * @return the expression engine
+ */
+ public ExpressionEngine getExpressionEngine()
+ {
+ return getParent().getExpressionEngine();
+ }
+
+ /**
+ * Sets the expression engine. This method sets the expression engine at the
+ * parent.
+ *
+ * @param expressionEngine the new expression engine
+ */
+ public void setExpressionEngine(ExpressionEngine expressionEngine)
+ {
+ getParent().setExpressionEngine(expressionEngine);
+ }
+
+ /**
+ * Returns the list delimiter. This method returns the parent's delimiter.
+ *
+ * @return the list delimiter
+ */
+ public char getListDelimiter()
+ {
+ return getParent().getListDelimiter();
+ }
+
+ /**
+ * Sets the list delimiter. The delimiter will also be set for the parent.
+ *
+ * @param listDelimiter the new delimiter
+ */
+ public void setListDelimiter(char listDelimiter)
+ {
+ getParent().setListDelimiter(listDelimiter);
+ }
+
+ /**
+ * Returns a flag if list properties should be splitted when they are added.
+ * This implementation returns the corresponding flag of the parent.
+ *
+ * @return the delimiter parsing flag
+ */
+ public boolean isDelimiterParsingDisabled()
+ {
+ return getParent().isDelimiterParsingDisabled();
+ }
+
+ /**
+ * Sets the delimiter parsing disabled flag. This method will also set this
+ * flag at the parent.
+ *
+ * @param delimiterParsingDisabled the new value of the flag
+ */
+ public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
+ {
+ getParent().setDelimiterParsingDisabled(delimiterParsingDisabled);
+ }
+
+ /**
+ * Returns the throw exception on missing flag. This implementation returns
+ * the value of the corresponding flag of the parent.
+ *
+ * @return the throw exception on missing flag
+ */
+ public boolean isThrowExceptionOnMissing()
+ {
+ return getParent().isThrowExceptionOnMissing();
+ }
+
+ /**
+ * Sets the throw exception on missing flag. The value is also set for the
+ * parent.
+ *
+ * @param throwExceptionOnMissing the new value of the flag
+ */
+ public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
+ {
+ getParent().setThrowExceptionOnMissing(throwExceptionOnMissing);
+ }
+
+ /**
+ * Creates a new node. This task is delegated to the parent.
+ *
+ * @param name the node's name
+ * @return the new node
+ */
+ protected Node createNode(String name)
+ {
+ return getParent().createNode(name);
+ }
+}
Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/SubnodeConfiguration.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java?rev=371084&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java Sat Jan 21 08:54:24 2006
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2006 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 java.util.HashSet;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for SubnodeConfiguration.
+ *
+ * @author Oliver Heger
+ * @version $Id$
+ */
+public class TestSubnodeConfiguration extends TestCase
+{
+ /** An array with names of tables (test data). */
+ private static final String[] TABLE_NAMES =
+ { "documents", "users" };
+
+ /** An array with the fields of the test tables (test data). */
+ private static final String[][] TABLE_FIELDS =
+ {
+ { "docid", "docname", "author", "dateOfCreation", "version", "size" },
+ { "userid", "uname", "firstName", "lastName" } };
+
+ /** The parent configuration. */
+ HierarchicalConfiguration parent;
+
+ /** The subnode configuration to be tested. */
+ SubnodeConfiguration config;
+
+ /** Stores the root node of the subnode config. */
+ ConfigurationNode subnode;
+
+ /** Stores a counter for the created nodes. */
+ int nodeCounter;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ parent = setUpParentConfig();
+ nodeCounter = 0;
+ }
+
+ /**
+ * Tests creation of a subnode config.
+ */
+ public void testInitSubNodeConfig()
+ {
+ setUpSubnodeConfig();
+ assertSame("Wrong root node in subnode", getSubnodeRoot(parent), config
+ .getRoot());
+ assertSame("Wrong parent config", parent, config.getParent());
+ }
+
+ /**
+ * Tests constructing a subnode configuration with a null parent. This
+ * should cause an exception.
+ */
+ public void testInitSubNodeConfigWithNullParent()
+ {
+ try
+ {
+ config = new SubnodeConfiguration(null, getSubnodeRoot(parent));
+ fail("Could set a null parent config!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests constructing a subnode configuration with a null root node. This
+ * should cause an exception.
+ */
+ public void testInitSubNodeConfigWithNullNode()
+ {
+ try
+ {
+ config = new SubnodeConfiguration(parent, null);
+ fail("Could set a null root node!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests if properties of the sub node can be accessed.
+ */
+ public void testGetProperties()
+ {
+ setUpSubnodeConfig();
+ assertEquals("Wrong table name", TABLE_NAMES[0], config
+ .getString("name"));
+ List fields = config.getList("fields.field.name");
+ assertEquals("Wrong number of fields", TABLE_FIELDS[0].length, fields
+ .size());
+ for (int i = 0; i < TABLE_FIELDS[0].length; i++)
+ {
+ assertEquals("Wrong field at position " + i, TABLE_FIELDS[0][i],
+ fields.get(i));
+ }
+ }
+
+ /**
+ * Tests setting of properties in both the parent and the subnode
+ * configuration and whether the changes are visible to each other.
+ */
+ public void testSetProperty()
+ {
+ setUpSubnodeConfig();
+ config.setProperty(null, "testTable");
+ config.setProperty("name", TABLE_NAMES[0] + "_tested");
+ assertEquals("Root value was not set", "testTable", parent
+ .getString("tables.table(0)"));
+ assertEquals("Table name was not changed", TABLE_NAMES[0] + "_tested",
+ parent.getString("tables.table(0).name"));
+
+ parent.setProperty("tables.table(0).fields.field(1).name", "testField");
+ assertEquals("Field name was not changed", "testField", config
+ .getString("fields.field(1).name"));
+ }
+
+ /**
+ * Tests adding of properties.
+ */
+ public void testAddProperty()
+ {
+ setUpSubnodeConfig();
+ config.addProperty("[@table-type]", "test");
+ assertEquals("parent.createNode() was not called", 1, nodeCounter);
+ assertEquals("Attribute not set", "test", parent
+ .getString("tables.table(0)[@table-type]"));
+
+ parent.addProperty("tables.table(0).fields.field(-1).name", "newField");
+ List fields = config.getList("fields.field.name");
+ assertEquals("New field was not added", TABLE_FIELDS[0].length + 1,
+ fields.size());
+ assertEquals("Wrong last field", "newField", fields
+ .get(fields.size() - 1));
+ }
+
+ /**
+ * Tests listing the defined keys.
+ */
+ public void testGetKeys()
+ {
+ setUpSubnodeConfig();
+ Set keys = new HashSet();
+ CollectionUtils.addAll(keys, config.getKeys());
+ assertEquals("Incorrect number of keys", 2, keys.size());
+ assertTrue("Key 1 not contained", keys.contains("name"));
+ assertTrue("Key 2 not contained", keys.contains("fields.field.name"));
+ }
+
+ /**
+ * Tests setting the exception on missing flag. The subnode config obtains
+ * this flag from its parent.
+ */
+ public void testSetThrowExceptionOnMissing()
+ {
+ setUpSubnodeConfig();
+ parent.setThrowExceptionOnMissing(true);
+ assertTrue("Exception flag not fetchted from parent", config
+ .isThrowExceptionOnMissing());
+ try
+ {
+ config.getString("non existing key");
+ fail("Could fetch non existing key!");
+ }
+ catch (NoSuchElementException nex)
+ {
+ // ok
+ }
+
+ config.setThrowExceptionOnMissing(false);
+ assertFalse("Exception flag not set on parent", parent
+ .isThrowExceptionOnMissing());
+ }
+
+ /**
+ * Tests handling of the delimiter parsing disabled flag. This is shared
+ * with the parent, too.
+ */
+ public void testSetDelimiterParsingDisabled()
+ {
+ setUpSubnodeConfig();
+ parent.setDelimiterParsingDisabled(true);
+ assertTrue("Delimiter parsing flag was not received from parent",
+ config.isDelimiterParsingDisabled());
+ config.addProperty("newProp", "test1,test2,test3");
+ assertEquals("New property was splitted", "test1,test2,test3", parent
+ .getString("tables.table(0).newProp"));
+ config.setDelimiterParsingDisabled(false);
+ assertFalse("Delimiter parsing flag was not set on parent", parent
+ .isDelimiterParsingDisabled());
+ }
+
+ /**
+ * Tests manipulating the list delimiter. This piece of data is used by both
+ * the parent and the subnode.
+ */
+ public void testSetListDelimiter()
+ {
+ setUpSubnodeConfig();
+ parent.setListDelimiter('/');
+ assertEquals("List delimiter not obtained from parent", '/', config
+ .getListDelimiter());
+ config.addProperty("newProp", "test1,test2/test3");
+ assertEquals("List was incorrectly splitted", "test1,test2", parent
+ .getString("tables.table(0).newProp"));
+ config.setListDelimiter(',');
+ assertEquals("List delimiter not set at parent", ',', parent
+ .getListDelimiter());
+ }
+
+ /**
+ * Tests changing the expression engine.
+ */
+ public void testSetExpressionEngine()
+ {
+ setUpSubnodeConfig();
+ parent.setExpressionEngine(new XPathExpressionEngine());
+ assertEquals("Wrong field name", TABLE_FIELDS[0][1], config
+ .getString("fields/field[2]/name"));
+ Set keys = new HashSet();
+ CollectionUtils.addAll(keys, config.getKeys());
+ assertEquals("Wrong number of keys", 2, keys.size());
+ assertTrue("Key 1 not contained", keys.contains("name"));
+ assertTrue("Key 2 not contained", keys.contains("fields/field/name"));
+ config.setExpressionEngine(null);
+ assertSame("Expression engine not set on parent",
+ HierarchicalConfiguration.getDefaultExpressionEngine(), parent
+ .getExpressionEngine());
+ }
+
+ /**
+ * Initializes the parent configuration. This method creates the typical
+ * structure of tables and fields nodes.
+ *
+ * @return the parent configuration
+ */
+ protected HierarchicalConfiguration setUpParentConfig()
+ {
+ HierarchicalConfiguration conf = new HierarchicalConfiguration()
+ {
+ // Provide a special implementation of createNode() to check
+ // if it is called by the subnode config
+ protected Node createNode(String name)
+ {
+ nodeCounter++;
+ return super.createNode(name);
+ }
+
+ };
+ for (int i = 0; i < TABLE_NAMES.length; i++)
+ {
+ conf.addProperty("tables.table(-1).name", TABLE_NAMES[i]);
+ for (int j = 0; j < TABLE_FIELDS[i].length; j++)
+ {
+ conf.addProperty("tables.table.fields.field(-1).name",
+ TABLE_FIELDS[i][j]);
+ }
+ }
+ return conf;
+ }
+
+ /**
+ * Returns the root node for the subnode config. This method returns the
+ * first table node.
+ *
+ * @param conf the parent config
+ * @return the root node for the subnode config
+ */
+ protected HierarchicalConfiguration.Node getSubnodeRoot(
+ HierarchicalConfiguration conf)
+ {
+ ConfigurationNode root = conf.getRoot();
+ return (HierarchicalConfiguration.Node) root.getChild(0).getChild(0);
+ }
+
+ /**
+ * Performs a standard initialization of the subnode config to test.
+ */
+ protected void setUpSubnodeConfig()
+ {
+ config = new SubnodeConfiguration(parent, getSubnodeRoot(parent));
+ }
+}
Propchange: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestSubnodeConfiguration.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org