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 2008/03/08 21:42:33 UTC
svn commit: r635081 [1/2] - in
/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2:
AbstractHierarchicalConfiguration.java InMemoryConfiguration.java
SubConfiguration.java
Author: oheger
Date: Sat Mar 8 12:42:32 2008
New Revision: 635081
URL: http://svn.apache.org/viewvc?rev=635081&view=rev
Log:
Initial implementation of hierarchical configurations that are based on node handlers
Added:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java
- copied, changed from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/InMemoryConfiguration.java
- copied, changed from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubConfiguration.java
- copied, changed from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubnodeConfiguration.java
Copied: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java (from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java?p2=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java&p1=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java&r1=632835&r2=635081&rev=635081&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/AbstractHierarchicalConfiguration.java Sat Mar 8 12:42:32 2008
@@ -14,210 +14,93 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.commons.configuration2;
-import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.apache.commons.configuration2.event.ConfigurationEvent;
import org.apache.commons.configuration2.event.ConfigurationListener;
-import org.apache.commons.configuration2.tree.ConfigurationNode;
-import org.apache.commons.configuration2.tree.ConfigurationNodeVisitor;
-import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter;
-import org.apache.commons.configuration2.tree.DefaultConfigurationNode;
-import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
-import org.apache.commons.configuration2.tree.ExpressionEngine;
-import org.apache.commons.configuration2.tree.NodeAddData;
+import org.apache.commons.configuration2.expr.DefaultExpressionEngine;
+import org.apache.commons.configuration2.expr.ExpressionEngine;
+import org.apache.commons.configuration2.expr.NodeAddData;
+import org.apache.commons.configuration2.expr.NodeHandler;
+import org.apache.commons.configuration2.expr.NodeList;
+import org.apache.commons.configuration2.expr.NodeVisitor;
+import org.apache.commons.configuration2.expr.NodeVisitorAdapter;
/**
- * <p>A specialized configuration class that extends its base class by the
- * ability of keeping more structure in the stored properties.</p><p>There
- * are some sources of configuration data that cannot be stored very well in a
- * <code>BaseConfiguration</code> object because then their structure is lost.
- * This is especially true for XML documents. This class can deal with such
- * structured configuration sources by storing the properties in a tree-like
- * organization.</p><p>The internal used storage form allows for a more
- * sophisticated access to single properties. As an example consider the
- * following XML document:</p><p>
- *
- * <pre>
- * <database>
- * <tables>
- * <table>
- * <name>users</name>
- * <fields>
- * <field>
- * <name>lid</name>
- * <type>long</name>
- * </field>
- * <field>
- * <name>usrName</name>
- * <type>java.lang.String</type>
- * </field>
- * ...
- * </fields>
- * </table>
- * <table>
- * <name>documents</name>
- * <fields>
- * <field>
- * <name>docid</name>
- * <type>long</type>
- * </field>
- * ...
- * </fields>
- * </table>
- * ...
- * </tables>
- * </database>
- * </pre>
- *
- * </p><p>If this document is parsed and stored in a
- * <code>HierarchicalConfiguration</code> object (which can be done by one of
- * the sub classes), there are enhanced possibilities of accessing properties.
- * The keys for querying information can contain indices that select a certain
- * element if there are multiple hits.</p><p>For instance the key
- * <code>tables.table(0).name</code> can be used to find out the name of the
- * first table. In opposite <code>tables.table.name</code> would return a
- * collection with the names of all available tables. Similarly the key
- * <code>tables.table(1).fields.field.name</code> returns a collection with
- * the names of all fields of the second table. If another index is added after
- * the <code>field</code> element, a single field can be accessed:
- * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
- * <code>getMaxIndex()</code> method that returns the maximum allowed index
- * that can be added to a given property key. This method can be used to iterate
- * over all values defined for a certain property.</p>
- * <p>Since the 1.3 release of <em>Commons Configuration</em> hierarchical
- * configurations support an <em>expression engine</em>. This expression engine
- * is responsible for evaluating the passed in configuration keys and map them
- * to the stored properties. The examples above are valid for the default
- * expression engine, which is used when a new <code>HierarchicalConfiguration</code>
- * instance is created. With the <code>setExpressionEngine()</code> method a
- * different expression engine can be set. For instance with
- * <code>{@link org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine}</code>
- * there is an expression engine available that supports configuration keys in
- * XPATH syntax.</p>
- * <p>In addition to the events common for all configuration classes hierarchical
- * configurations support some more events that correspond to some specific
- * methods and features:
- * <dl><dt><em>EVENT_ADD_NODES</em></dt><dd>The <code>addNodes()</code> method
- * was called; the event object contains the key, to which the nodes were added,
- * and a collection with the new nodes as value.</dd>
- * <dt><em>EVENT_CLEAR_TREE</em></dt><dd>The <code>clearTree()</code> method was
- * called; the event object stores the key of the removed sub tree.</dd>
- * <dt><em>EVENT_SUBNODE_CHANGED</em></dt><dd>A <code>SubnodeConfiguration</code>
- * that was created from this configuration has been changed. The value property
- * of the event object contains the original event object as it was sent by the
- * subnode configuration.</dd></dl></p>
- * <p><em>Note:</em>Configuration objects of this type can be read concurrently
- * by multiple threads. However if one of these threads modifies the object,
- * synchronization has to be performed manually.</p>
+ * <p>A base class for hierarchical configurations.</p>
+ * <p>This class implements the major part of the functionality required for
+ * dealing with hierarchical node structures. It provides fundamental algorithms
+ * for traversing and manipulating such structures. Access to the node objects
+ * is controlled by a <code>{@link NodeHandler}</code> object; therefore this
+ * base class can operate on arbitrary node types. (By making use of Java generics,
+ * this can even be achieved in a type-safe manner.)</p>
+ * <p>Concrete subclasses must initialize this base class with an appropriate
+ * <code>{@link NodeHandler}</code> instance. They also have to define a method
+ * that returns the root node of the maintained node hierarchy.</p>
*
* @author Oliver Heger
* @version $Id$
+ * @since 2.0
+ * @param <T> the type of the nodes this configuration deals with
*/
-public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
+public abstract class AbstractHierarchicalConfiguration<T> extends AbstractConfiguration implements Cloneable
{
/**
* Constant for the clear tree event.
- * @since 1.3
*/
public static final int EVENT_CLEAR_TREE = 10;
/**
- * Constant for the add nodes event.
- * @since 1.3
- */
- public static final int EVENT_ADD_NODES = 11;
-
- /**
* Constant for the subnode configuration modified event.
- * @since 1.5
*/
public static final int EVENT_SUBNODE_CHANGED = 12;
- /**
- * The serial version UID.
- */
- private static final long serialVersionUID = 3373812230395363192L;
-
/** Stores the default expression engine to be used for new objects.*/
private static ExpressionEngine defaultExpressionEngine;
- /** Stores the root configuration node.*/
- private ConfigurationNode rootNode;
-
/** Stores the expression engine for this instance.*/
- private transient ExpressionEngine expressionEngine;
+ private ExpressionEngine expressionEngine;
+
+ /** Stores the node handler for accessing the internally used nodes.*/
+ private NodeHandler<T> nodeHandler;
/**
* Creates a new instance of <code>HierarchicalConfiguration</code>.
*/
- public HierarchicalConfiguration()
+ protected AbstractHierarchicalConfiguration(NodeHandler<T> handler)
{
- setRootNode(new DefaultConfigurationNode());
+ nodeHandler = handler;
}
/**
- * Creates a new instance of <code>HierarchicalConfiguration</code> and
- * copies all data contained in the specified configuration into the new
- * one.
+ * Returns the <code>NodeHandler</code> used by this configuration.
*
- * @param c the configuration that is to be copied (if <b>null</b>, this
- * constructor will behave like the standard constructor)
- * @since 1.4
+ * @return the node handler
*/
- public HierarchicalConfiguration(HierarchicalConfiguration c)
+ public NodeHandler<T> getNodeHandler()
{
- this();
- if (c != null)
- {
- CloneVisitor visitor = new CloneVisitor();
- c.getRootNode().visit(visitor);
- setRootNode(visitor.getClone());
- }
+ return nodeHandler;
}
/**
* Returns the root node of this hierarchical configuration.
*
* @return the root node
- * @since 1.3
*/
- public ConfigurationNode getRootNode()
- {
- return rootNode;
- }
-
- /**
- * Sets the root node of this hierarchical configuration.
- *
- * @param rootNode the root node
- * @since 1.3
- */
- public void setRootNode(ConfigurationNode rootNode)
- {
- if (rootNode == null)
- {
- throw new IllegalArgumentException("Root node must not be null!");
- }
- this.rootNode = rootNode;
- }
+ public abstract T getRootNode();
/**
* Returns the default expression engine.
*
* @return the default expression engine
- * @since 1.3
*/
public static synchronized ExpressionEngine getDefaultExpressionEngine()
{
@@ -235,7 +118,6 @@
* impact all instances, for which no specific engine is set.
*
* @param engine the new default expression engine
- * @since 1.3
*/
public static synchronized void setDefaultExpressionEngine(ExpressionEngine engine)
{
@@ -252,7 +134,6 @@
* the default expression engine will be returned.
*
* @return the current expression engine
- * @since 1.3
*/
public ExpressionEngine getExpressionEngine()
{
@@ -266,7 +147,6 @@
*
* @param expressionEngine the new expression engine; can be <b>null</b>,
* then the default expression engine will be used
- * @since 1.3
*/
public void setExpressionEngine(ExpressionEngine expressionEngine)
{
@@ -282,7 +162,7 @@
*/
public Object getProperty(String key)
{
- List<ConfigurationNode> nodes = fetchNodeList(key);
+ NodeList<T> nodes = fetchNodeList(key);
if (nodes.size() == 0)
{
@@ -291,11 +171,12 @@
else
{
List<Object> list = new ArrayList<Object>();
- for (ConfigurationNode node : nodes)
+ for (int i = 0; i < nodes.size(); i++)
{
- if (node.getValue() != null)
+ Object value = nodes.getValue(i, getNodeHandler());
+ if (value != null)
{
- list.add(node.getValue());
+ list.add(value);
}
}
@@ -321,81 +202,9 @@
@Override
protected void addPropertyDirect(String key, Object obj)
{
- NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
- ConfigurationNode node = processNodeAddData(data);
- node.setValue(obj);
- }
-
- /**
- * Adds a collection of nodes at the specified position of the configuration
- * tree. This method works similar to <code>addProperty()</code>, but
- * instead of a single property a whole collection of nodes can be added -
- * and thus complete configuration sub trees. E.g. with this method it is
- * possible to add parts of another <code>HierarchicalConfiguration</code>
- * object to this object. (However be aware that a
- * <code>ConfigurationNode</code> object can only belong to a single
- * configuration. So if nodes from one configuration are directly added to
- * another one using this method, the structure of the source configuration
- * will be broken. In this case you should clone the nodes to be added
- * before calling <code>addNodes()</code>.) If the passed in key refers to
- * an existing and unique node, the new nodes are added to this node.
- * Otherwise a new node will be created at the specified position in the
- * hierarchy.
- *
- * @param key the key where the nodes are to be added; can be <b>null </b>,
- * then they are added to the root node
- * @param nodes a collection with the <code>Node</code> objects to be
- * added
- */
- public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
- {
- if (nodes == null || nodes.isEmpty())
- {
- return;
- }
-
- fireEvent(EVENT_ADD_NODES, key, nodes, true);
- ConfigurationNode parent;
- List<ConfigurationNode> target = fetchNodeList(key);
- if (target.size() == 1)
- {
- // existing unique key
- parent = target.get(0);
- }
- else
- {
- // otherwise perform an add operation
- parent = processNodeAddData(getExpressionEngine().prepareAdd(getRootNode(), key));
- }
-
- if (parent.isAttribute())
- {
- throw new IllegalArgumentException("Cannot add nodes to an attribute node!");
- }
-
- // a visitor to ensure that the nodes' references are cleared; this is
- // necessary if the nodes are moved from another configuration
- ConfigurationNodeVisitor clearRefVisitor = new ConfigurationNodeVisitorAdapter()
- {
- public void visitBeforeChildren(ConfigurationNode node)
- {
- node.setReference(null);
- }
- };
-
- for (ConfigurationNode child : nodes)
- {
- if (child.isAttribute())
- {
- parent.addAttribute(child);
- }
- else
- {
- parent.addChild(child);
- }
- child.visit(clearRefVisitor);
- }
- fireEvent(EVENT_ADD_NODES, key, nodes, false);
+ NodeAddData<T> data = getExpressionEngine().prepareAdd(getRootNode(),
+ key, getNodeHandler());
+ processNodeAddData(data, obj);
}
/**
@@ -410,77 +219,8 @@
}
/**
- * 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. The nodes selected by the prefix (it is possible
- * that multiple nodes are selected) are mapped to the root node of the
- * returned configuration, i.e. their children and attributes will become
- * children and attributes of the new root node. However a value of the root
- * node is only set if exactly one of the selected nodes contain a value (if
- * multiple nodes have a value, there is simply no way to decide how these
- * values are merged together). Note that the returned
- * <code>Configuration</code> object is not connected to its source
- * configuration: updates on the source configuration are not reflected in
- * the subset and vice versa.
- *
- * @param prefix the prefix of the keys for the subset
- * @return a new configuration object representing the selected subset
- */
- @Override
- @SuppressWarnings("serial")
- public Configuration subset(String prefix)
- {
- Collection<ConfigurationNode> nodes = fetchNodeList(prefix);
- if (nodes.isEmpty())
- {
- return new HierarchicalConfiguration();
- }
-
- final HierarchicalConfiguration parent = this;
- HierarchicalConfiguration result = new HierarchicalConfiguration()
- {
- // Override interpolate to always interpolate on the parent
- protected Object interpolate(Object value)
- {
- return parent.interpolate(value);
- }
- };
- CloneVisitor visitor = new CloneVisitor();
-
- // Initialize the new root node
- Object value = null;
- int valueCount = 0;
- for (ConfigurationNode nd : nodes)
- {
- if (nd.getValue() != null)
- {
- value = nd.getValue();
- valueCount++;
- }
- nd.visit(visitor);
-
- for (ConfigurationNode child : visitor.getClone().getChildren())
- {
- result.getRootNode().addChild(child);
- }
- for (ConfigurationNode attr : visitor.getClone().getAttributes())
- {
- result.getRootNode().addAttribute(attr);
- }
- }
-
- // Determine the value of the new root
- if (valueCount == 1)
- {
- result.getRootNode().setValue(value);
- }
- return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
- }
-
- /**
* <p>
- * Returns a hierarchical subnode configuration object that wraps the
+ * Returns a hierarchical sub configuration object that wraps the
* configuration node specified by the given key. This method provides an
* easy means of accessing sub trees of a hierarchical configuration. In the
* returned configuration the sub tree can directly be accessed, it becomes
@@ -494,58 +234,56 @@
* <code>subset()</code> supports arbitrary subsets of configuration nodes
* while <code>configurationAt()</code> only returns a single sub tree.
* Please refer to the documentation of the
- * <code>SubnodeConfiguration</code> class to obtain further information
- * about subnode configurations and when they should be used.
+ * <code>{@link SubConfiguration}</code> class to obtain further information
+ * about sub configurations and when they should be used.
* </p>
* <p>
* With the <code>supportUpdate</code> flag the behavior of the returned
- * <code>SubnodeConfiguration</code> regarding updates of its parent
- * configuration can be determined. A subnode configuration operates on the
+ * <code>SubConfiguration</code> regarding updates of its parent
+ * configuration can be determined. A sub configuration operates on the
* same nodes as its parent, so changes at one configuration are normally
* directly visible for the other configuration. There are however changes
- * of the parent configuration, which are not recognized by the subnode
+ * of the parent configuration, which are not recognized by the sub
* configuration per default. An example for this is a reload operation (for
* file-based configurations): Here the complete node set of the parent
- * configuration is replaced, but the subnode configuration still references
- * the old nodes. If such changes should be detected by the subnode
+ * configuration is replaced, but the sub configuration still references
+ * the old nodes. If such changes should be detected by the sub
* configuration, the <code>supportUpdates</code> flag must be set to
- * <b>true</b>. This causes the subnode configuration to reevaluate the key
+ * <b>true</b>. This causes the sub configuration to reevaluate the key
* used for its creation each time it is accessed. This guarantees that the
- * subnode configuration always stays in sync with its key, even if the
+ * sub configuration always stays in sync with its key, even if the
* parent configuration's data significantly changes. If such a change
* makes the key invalid - because it now no longer points to exactly one
- * node -, the subnode configuration is not reconstructed, but keeps its
+ * node -, the sub configuration is not reconstructed, but keeps its
* old data. It is then quasi detached from its parent.
* </p>
*
* @param key the key that selects the sub tree
- * @param supportUpdates a flag whether the returned subnode configuration
+ * @param supportUpdates a flag whether the returned sub configuration
* should be able to handle updates of its parent
* @return a hierarchical configuration that contains this sub tree
- * @see SubnodeConfiguration
- * @since 1.5
+ * @see SubConfiguration
*/
- public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
+ public AbstractHierarchicalConfiguration<T> configurationAt(String key, boolean supportUpdates)
{
- List<ConfigurationNode> nodes = fetchNodeList(key);
- if (nodes.size() != 1)
+ NodeList<T> nodes = fetchNodeList(key);
+ if (nodes.size() != 1 || !nodes.isNode(0))
{
throw new IllegalArgumentException("Passed in key must select exactly one node: " + key);
}
- return supportUpdates ? createSubnodeConfiguration(nodes.get(0), key) : createSubnodeConfiguration(nodes.get(0));
+ return supportUpdates ? createSubnodeConfiguration(nodes.getNode(0), key) : createSubnodeConfiguration(nodes.getNode(0));
}
/**
- * Returns a hierarchical subnode configuration for the node specified by
+ * Returns a hierarchical sub configuration for the node specified by
* the given key. This is a short form for <code>configurationAt(key,
* <b>false</b>)</code>.
- *
+ *
* @param key the key that selects the sub tree
* @return a hierarchical configuration that contains this sub tree
- * @see SubnodeConfiguration
- * @since 1.3
+ * @see SubConfiguration
*/
- public SubnodeConfiguration configurationAt(String key)
+ public AbstractHierarchicalConfiguration<T> configurationAt(String key)
{
return configurationAt(key, false);
}
@@ -575,48 +313,46 @@
* @param key the key for selecting the desired nodes
* @return a list with hierarchical configuration objects; each
* configuration represents one of the nodes selected by the passed in key
- * @since 1.3
*/
- public List<HierarchicalConfiguration> configurationsAt(String key)
+ public List<AbstractHierarchicalConfiguration<T>> configurationsAt(String key)
{
- List<ConfigurationNode> nodes = fetchNodeList(key);
- List<HierarchicalConfiguration> configs = new ArrayList<HierarchicalConfiguration>(nodes.size());
- for (ConfigurationNode node : nodes)
+ NodeList<T> nodes = fetchNodeList(key);
+ List<AbstractHierarchicalConfiguration<T>> configs = new ArrayList<AbstractHierarchicalConfiguration<T>>(
+ nodes.size());
+ for (int index = 0; index < nodes.size(); index++)
{
- configs.add(createSubnodeConfiguration(node));
+ configs.add(createSubnodeConfiguration(nodes.getNode(index)));
}
return configs;
}
/**
- * Creates a subnode configuration for the specified node. This method is
+ * Creates a sub configuration for the specified node. This method is
* called by <code>configurationAt()</code> and
* <code>configurationsAt()</code>.
*
- * @param node the node, for which a subnode configuration is to be created
+ * @param node the node, for which a sub configuration is to be created
* @return the configuration for the given node
- * @since 1.3
*/
- protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
+ protected SubConfiguration<T> createSubnodeConfiguration(T node)
{
- SubnodeConfiguration result = new SubnodeConfiguration(this, node);
+ SubConfiguration<T> result = new SubConfiguration<T>(this, node);
registerSubnodeConfiguration(result);
return result;
}
/**
- * Creates a new subnode configuration for the specified node and sets its
- * construction key. A subnode configuration created this way will be aware
+ * Creates a new sub configuration for the specified node and sets its
+ * construction key. A sub configuration created this way will be aware
* of structural changes of its parent.
*
- * @param node the node, for which a subnode configuration is to be created
+ * @param node the node, for which a sub configuration is to be created
* @param subnodeKey the key used to construct the configuration
* @return the configuration for the given node
- * @since 1.5
*/
- protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node, String subnodeKey)
+ protected SubConfiguration<T> createSubnodeConfiguration(T node, String subnodeKey)
{
- SubnodeConfiguration result = createSubnodeConfiguration(node);
+ SubConfiguration<T> result = createSubnodeConfiguration(node);
result.setSubnodeKey(subnodeKey);
return result;
}
@@ -628,7 +364,6 @@
* and notifies the registered listeners.
*
* @param event the event describing the change
- * @since 1.5
*/
protected void subnodeConfigurationChanged(ConfigurationEvent event)
{
@@ -636,14 +371,13 @@
}
/**
- * Registers this instance at the given subnode configuration. This
+ * Registers this instance at the given sub configuration. This
* implementation will register a change listener, so that modifications of
- * the subnode configuration can be tracked.
+ * the sub configuration can be tracked.
*
- * @param config the subnode configuration
- * @since 1.5
+ * @param config the sub configuration
*/
- void registerSubnodeConfiguration(SubnodeConfiguration config)
+ void registerSubnodeConfiguration(SubConfiguration<T> config)
{
config.addConfigurationListener(new ConfigurationListener()
{
@@ -681,7 +415,7 @@
fireEvent(EVENT_SET_PROPERTY, key, value, true);
// Update the existing nodes for this property
- Iterator<ConfigurationNode> itNodes = fetchNodeList(key).iterator();
+ NodeList<T> nodes = fetchNodeList(key);
Iterator<?> itValues;
if (!isDelimiterParsingDisabled())
{
@@ -692,9 +426,11 @@
itValues = Collections.singleton(value).iterator();
}
- while (itNodes.hasNext() && itValues.hasNext())
+ int index = 0;
+ while (index < nodes.size() && itValues.hasNext())
{
- itNodes.next().setValue(itValues.next());
+ nodes.setValue(index, itValues.next(), getNodeHandler());
+ index++;
}
// Add additional nodes if necessary
@@ -704,9 +440,9 @@
}
// Remove remaining nodes
- while (itNodes.hasNext())
+ while (index < nodes.size())
{
- clearNode(itNodes.next());
+ removeListElement(nodes, index++, true);
}
fireEvent(EVENT_SET_PROPERTY, key, value, false);
@@ -722,14 +458,7 @@
*/
public void clearTree(String key)
{
- fireEvent(EVENT_CLEAR_TREE, key, null, true);
- List<ConfigurationNode> nodes = fetchNodeList(key);
-
- for (ConfigurationNode node : nodes)
- {
- removeNode(node);
- }
- fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
+ removeNodeList(key, EVENT_CLEAR_TREE, false);
}
/**
@@ -742,15 +471,57 @@
@Override
public void clearProperty(String key)
{
- fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
- List<ConfigurationNode> nodes = fetchNodeList(key);
+ removeNodeList(key, EVENT_CLEAR_PROPERTY, true);
+ }
- for (ConfigurationNode node : nodes)
+ /**
+ * Removes the list element with the specified index from this
+ * configuration. This method calls the appropriate remove method depending
+ * on the type of the list element.
+ *
+ * @param nodes the node list
+ * @param index the index
+ * @param clear a flag whether the element should only be cleared or completely removed
+ */
+ private void removeListElement(NodeList<T> nodes, int index, boolean clear)
+ {
+ if (nodes.isNode(index))
+ {
+ if (clear)
+ {
+ clearNode(nodes.getNode(index));
+ }
+ else
+ {
+ removeNode(nodes.getNode(index));
+ }
+ }
+ else
{
- clearNode(node);
+ T parent = nodes.getAttributeParent(index);
+ getNodeHandler().removeAttribute(parent,
+ nodes.getName(index, getNodeHandler()));
+ removeNodeIfUndefined(parent);
}
+ }
+
+ /**
+ * Removes or clears all nodes or attributes matched by the given key.
+ * @param key the key
+ * @param event the event to fire
+ * @param clear determines whether the elements are cleared or removed
+ */
+ private void removeNodeList(String key, int event, boolean clear)
+ {
+ fireEvent(event, key, null, true);
+ NodeList<T> nodes = fetchNodeList(key);
- fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
+ for (int index = 0; index < nodes.size(); index++)
+ {
+ removeListElement(nodes, index, clear);
+ }
+
+ fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
}
/**
@@ -763,7 +534,7 @@
public Iterator<String> getKeys()
{
DefinedKeysVisitor visitor = new DefinedKeysVisitor();
- getRootNode().visit(visitor);
+ visit(getRootNode(), visitor);
return visitor.getKeyList().iterator();
}
@@ -780,17 +551,17 @@
public Iterator<String> getKeys(String prefix)
{
DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
- List<ConfigurationNode> nodes = fetchNodeList(prefix);
+ NodeList<T> nodes = fetchNodeList(prefix);
- for (ConfigurationNode node : nodes)
+ for (int i = 0; i < nodes.size(); i++)
{
- for (ConfigurationNode child : node.getChildren())
- {
- child.visit(visitor);
- }
- for (ConfigurationNode attr : node.getAttributes())
+ if(nodes.isNode(i))
{
- attr.visit(visitor);
+ for(T child : getNodeHandler().getChildren(nodes.getNode(i)))
+ {
+ visit(child, visitor);
+ }
+ visitor.appendAttributes(nodes.getNode(i), prefix, getNodeHandler());
}
}
@@ -812,68 +583,39 @@
}
/**
- * Creates a copy of this object. This new configuration object will contain
- * copies of all nodes in the same structure. Registered event listeners
- * won't be cloned; so they are not registered at the returned copy.
+ * Helper method for fetching a list of all nodes that are addressed by the
+ * specified key.
*
- * @return the copy
- * @since 1.2
+ * @param key the key
+ * @return a list with all affected nodes (never <b>null </b>)
*/
- @Override
- public Object clone()
+ protected NodeList<T> fetchNodeList(String key)
{
- try
- {
- HierarchicalConfiguration copy = (HierarchicalConfiguration) super.clone();
-
- // clone the nodes, too
- CloneVisitor v = new CloneVisitor();
- getRootNode().visit(v);
- copy.setRootNode(v.getClone());
-
- return copy;
- }
- catch (CloneNotSupportedException cex)
- {
- // should not happen
- throw new ConfigurationRuntimeException(cex);
- }
+ return getExpressionEngine().query(getRootNode(), key, getNodeHandler());
}
/**
- * Returns a configuration with the same content as this configuration, but
- * with all variables replaced by their actual values. This implementation
- * is specific for hierarchical configurations. It clones the current
- * configuration and runs a specialized visitor on the clone, which performs
- * interpolation on the single configuration nodes.
+ * Visits the specified configuration node. This method implements the
+ * traversal of the node hierarchy starting with the specified node.
*
- * @return a configuration with all variables interpolated
- * @since 1.5
+ * @param node the node to be visited
+ * @param visitor the visitor
*/
- @Override
- public Configuration interpolatedConfiguration()
+ protected void visit(T node, NodeVisitor<T> visitor)
{
- HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
- c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
+ if (!visitor.terminate())
{
- public void visitAfterChildren(ConfigurationNode node)
+ visitor.visitBeforeChildren(node, getNodeHandler());
+
+ for (Iterator<T> it = getNodeHandler().getChildren(node).iterator(); it
+ .hasNext()
+ && !visitor.terminate();)
{
- node.setValue(interpolate(node.getValue()));
+ visit(it.next(), visitor);
}
- });
- return c;
- }
- /**
- * Helper method for fetching a list of all nodes that are addressed by the
- * specified key.
- *
- * @param key the key
- * @return a list with all affected nodes (never <b>null </b>)
- */
- protected List<ConfigurationNode> fetchNodeList(String key)
- {
- return getExpressionEngine().query(getRootNode(), key);
+ visitor.visitAfterChildren(node, getNodeHandler());
+ }
}
/**
@@ -882,10 +624,10 @@
* @param node the node to be checked
* @return a flag if this node is defined
*/
- protected boolean nodeDefined(ConfigurationNode node)
+ protected boolean nodeDefined(T node)
{
- DefinedVisitor visitor = new DefinedVisitor();
- node.visit(visitor);
+ DefinedVisitor<T> visitor = new DefinedVisitor<T>();
+ visit(node, visitor);
return visitor.isDefined();
}
@@ -896,16 +638,13 @@
*
* @param node the node to be removed
*/
- protected void removeNode(ConfigurationNode node)
+ protected void removeNode(T node)
{
- ConfigurationNode parent = node.getParentNode();
+ T parent = getNodeHandler().getParent(node);
if (parent != null)
{
- parent.removeChild(node);
- if (!nodeDefined(parent))
- {
- removeNode(parent);
- }
+ getNodeHandler().removeChild(parent, node);
+ removeNodeIfUndefined(parent);
}
}
@@ -915,9 +654,19 @@
*
* @param node the node to be cleared
*/
- protected void clearNode(ConfigurationNode node)
+ protected void clearNode(T node)
+ {
+ getNodeHandler().setValue(node, null);
+ removeNodeIfUndefined(node);
+ }
+
+ /**
+ * Removes the specified node if it is undefined.
+ *
+ * @param node the node
+ */
+ private void removeNodeIfUndefined(T node)
{
- node.setValue(null);
if (!nodeDefined(node))
{
removeNode(node);
@@ -925,81 +674,59 @@
}
/**
- * Creates a new <code>Node</code> object with the specified name. This
- * method can be overloaded in derived classes if a specific node type is
- * needed. This base implementation always returns a new object of the
- * <code>Node</code> class.
+ * Creates a new node object with the specified name. This base implementation
+ * delegates to the <code>NodeHandler</code> for creating a new node.
*
+ * @param parent the parent of the new node
* @param name the name of the new node
* @return the new node
*/
- protected ConfigurationNode createNode(String name)
+ protected T createNode(T parent, String name)
{
- return new DefaultConfigurationNode(name);
+ return getNodeHandler().addChild(parent, name);
}
/**
- * Helper method for processing a node add data object obtained from the
- * expression engine. This method will create all new nodes.
+ * Helper method for processing a <code>NodeAddData</code> object obtained from the
+ * expression engine. This method will create all new nodes and set the value
+ * of the last node, which represents the newly added property.
*
* @param data the data object
- * @return the new node
- * @since 1.3
+ * @param value the value of the new property
+ * @return the new node (<b>null</b> if an attribute was added)
*/
- private ConfigurationNode processNodeAddData(NodeAddData data)
+ protected T processNodeAddData(NodeAddData<T> data, Object value)
{
- ConfigurationNode node = data.getParent();
+ T node = data.getParent();
// Create missing nodes on the path
for (String nodeName : data.getPathNodes())
{
- ConfigurationNode child = createNode(nodeName);
- node.addChild(child);
+ T child = createNode(node, nodeName);
node = child;
}
- // Add new target node
- ConfigurationNode child = createNode(data.getNewNodeName());
+ // Add the new property
if (data.isAttribute())
{
- node.addAttribute(child);
+ getNodeHandler().setAttributeValue(node, data.getNewNodeName(),
+ value);
+ return null;
}
else
{
- node.addChild(child);
+ T child = createNode(node, data.getNewNodeName());
+ getNodeHandler().setValue(child, value);
+ return child;
}
- return child;
- }
-
- /**
- * Clears all reference fields in a node structure. A configuration node can
- * store a so-called "reference". The meaning of this data is
- * determined by a concrete sub class. Typically such references are
- * specific for a configuration instance. If this instance is cloned or
- * copied, they must be cleared. This can be done using this method.
- *
- * @param node the root node of the node hierarchy, in which the references
- * are to be cleared
- * @since 1.4
- */
- protected static void clearReferences(ConfigurationNode node)
- {
- node.visit(new ConfigurationNodeVisitorAdapter()
- {
- public void visitBeforeChildren(ConfigurationNode node)
- {
- node.setReference(null);
- }
- });
}
/**
* A specialized visitor that checks if a node is defined.
* "Defined" in this terms means that the node or at least one of
* its sub nodes is associated with a value.
- *
*/
- static class DefinedVisitor extends ConfigurationNodeVisitorAdapter
+ private static class DefinedVisitor<T> extends NodeVisitorAdapter<T>
{
/** Stores the defined flag. */
private boolean defined;
@@ -1022,9 +749,9 @@
* @param node the actual node
*/
@Override
- public void visitBeforeChildren(ConfigurationNode node)
+ public void visitBeforeChildren(T node, NodeHandler<T> handler)
{
- defined = node.getValue() != null;
+ defined = handler.isDefined(node);
}
/**
@@ -1042,7 +769,7 @@
* A specialized visitor that fills a list with keys that are defined in a
* node hierarchy.
*/
- class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
+ private class DefinedKeysVisitor extends NodeVisitorAdapter<T>
{
/** Stores the list to be filled. */
private Set<String> keyList;
@@ -1086,9 +813,10 @@
* node's key from the stack.
*
* @param node the node
+ * @param handler the node handler
*/
@Override
- public void visitAfterChildren(ConfigurationNode node)
+ public void visitAfterChildren(T node, NodeHandler<T> handler)
{
parentKeys.pop();
}
@@ -1098,191 +826,38 @@
* to the internal list.
*
* @param node the node to be visited
+ * @param handler the node handler
*/
@Override
- public void visitBeforeChildren(ConfigurationNode node)
+ public void visitBeforeChildren(T node, NodeHandler<T> handler)
{
String parentKey = parentKeys.isEmpty() ? null : (String) parentKeys.peek();
- String key = getExpressionEngine().nodeKey(node, parentKey);
+ String key = getExpressionEngine().nodeKey(node, parentKey, handler);
parentKeys.push(key);
- if (node.getValue() != null)
+ if (handler.getValue(node) != null)
{
keyList.add(key);
}
- }
- }
- /**
- * A specialized visitor that is able to create a deep copy of a node
- * hierarchy.
- */
- static class CloneVisitor extends ConfigurationNodeVisitorAdapter
- {
- /** A stack with the actual object to be copied. */
- private Stack<ConfigurationNode> copyStack;
-
- /** Stores the result of the clone process. */
- private ConfigurationNode result;
-
- /**
- * Creates a new instance of <code>CloneVisitor</code>.
- */
- public CloneVisitor()
- {
- copyStack = new Stack<ConfigurationNode>();
- }
-
- /**
- * Visits the specified node after its children have been processed.
- *
- * @param node the node
- */
- @Override
- public void visitAfterChildren(ConfigurationNode node)
- {
- ConfigurationNode copy = copyStack.pop();
- if (copyStack.isEmpty())
- {
- result = copy;
- }
+ appendAttributes(node, key, handler);
}
/**
- * Visits and copies the specified node.
+ * Adds the keys of the attributes of the given node to the internal key
+ * list.
*
- * @param node the node
+ * @param node the parent node
+ * @param parentKey the key of the parent node
+ * @param handler the node handler
*/
- @Override
- public void visitBeforeChildren(ConfigurationNode node)
+ public void appendAttributes(T node, String parentKey,
+ NodeHandler<T> handler)
{
- ConfigurationNode copy = (ConfigurationNode) node.clone();
- copy.setParentNode(null);
-
- if (!copyStack.isEmpty())
+ for (String attr : handler.getAttributes(node))
{
- if (node.isAttribute())
- {
- copyStack.peek().addAttribute(copy);
- }
- else
- {
- copyStack.peek().addChild(copy);
- }
+ keyList.add(getExpressionEngine().attributeKey(node, parentKey,
+ attr, handler));
}
-
- copyStack.push(copy);
}
-
- /**
- * Returns the result of the clone process. This is the root node of the
- * cloned node hierarchy.
- *
- * @return the cloned root node
- */
- public ConfigurationNode getClone()
- {
- return result;
- }
- }
-
- /**
- * A specialized visitor base class that can be used for storing the tree of
- * configuration nodes. The basic idea is that each node can be associated
- * with a reference object. This reference object has a concrete meaning in
- * a derived class, e.g. an entry in a JNDI context or an XML element. When
- * the configuration tree is set up, the <code>load()</code> method is
- * responsible for setting the reference objects. When the configuration
- * tree is later modified, new nodes do not have a defined reference object.
- * This visitor class processes all nodes and finds the ones without a
- * defined reference object. For those nodes the <code>insert()</code>
- * method is called, which must be defined in concrete sub classes. This
- * method can perform all steps to integrate the new node into the original
- * structure.
- */
- protected abstract static class BuilderVisitor extends ConfigurationNodeVisitorAdapter
- {
- /**
- * Visits the specified node before its children have been traversed.
- *
- * @param node the node to visit
- */
- @Override
- public void visitBeforeChildren(ConfigurationNode node)
- {
- Collection<ConfigurationNode> subNodes = new LinkedList<ConfigurationNode>(node.getChildren());
- subNodes.addAll(node.getAttributes());
- Iterator<ConfigurationNode> children = subNodes.iterator();
- ConfigurationNode sibling1 = null;
- ConfigurationNode nd = null;
-
- while (children.hasNext())
- {
- // find the next new node
- do
- {
- sibling1 = nd;
- nd = children.next();
- } while (nd.getReference() != null && children.hasNext());
-
- if (nd.getReference() == null)
- {
- // find all following new nodes
- List<ConfigurationNode> newNodes = new LinkedList<ConfigurationNode>();
- newNodes.add(nd);
- while (children.hasNext())
- {
- nd = children.next();
- if (nd.getReference() == null)
- {
- newNodes.add(nd);
- }
- else
- {
- break;
- }
- }
-
- // Insert all new nodes
- ConfigurationNode sibling2 = (nd.getReference() == null) ? null : nd;
- for (ConfigurationNode insertNode : newNodes)
- {
- if (insertNode.getReference() == null)
- {
- Object ref = insert(insertNode, node, sibling1, sibling2);
- if (ref != null)
- {
- insertNode.setReference(ref);
- }
- sibling1 = insertNode;
- }
- }
- }
- }
- }
-
- /**
- * Inserts a new node into the structure constructed by this builder.
- * This method is called for each node that has been added to the
- * configuration tree after the configuration has been loaded from its
- * source. These new nodes have to be inserted into the original
- * structure. The passed in nodes define the position of the node to be
- * inserted: its parent and the siblings between to insert. The return
- * value is interpreted as the new reference of the affected
- * <code>Node</code> object; if it is not <b>null </b>, it is passed
- * to the node's <code>setReference()</code> method.
- *
- * @param newNode the node to be inserted
- * @param parent the parent node
- * @param sibling1 the sibling after which the node is to be inserted;
- * can be <b>null </b> if the new node is going to be the first child
- * node
- * @param sibling2 the sibling before which the node is to be inserted;
- * can be <b>null </b> if the new node is going to be the last child
- * node
- * @return the reference object for the node to be inserted
- */
- protected abstract Object insert(ConfigurationNode newNode,
- ConfigurationNode parent, ConfigurationNode sibling1,
- ConfigurationNode sibling2);
}
}