commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject 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
Date Sat, 08 Mar 2008 20:42:33 GMT
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>
- * &lt;database&gt;
- *   &lt;tables&gt;
- *     &lt;table&gt;
- *       &lt;name&gt;users&lt;/name&gt;
- *       &lt;fields&gt;
- *         &lt;field&gt;
- *           &lt;name&gt;lid&lt;/name&gt;
- *           &lt;type&gt;long&lt;/name&gt;
- *         &lt;/field&gt;
- *         &lt;field&gt;
- *           &lt;name&gt;usrName&lt;/name&gt;
- *           &lt;type&gt;java.lang.String&lt;/type&gt;
- *         &lt;/field&gt;
- *        ...
- *       &lt;/fields&gt;
- *     &lt;/table&gt;
- *     &lt;table&gt;
- *       &lt;name&gt;documents&lt;/name&gt;
- *       &lt;fields&gt;
- *         &lt;field&gt;
- *           &lt;name&gt;docid&lt;/name&gt;
- *           &lt;type&gt;long&lt;/type&gt;
- *         &lt;/field&gt;
- *         ...
- *       &lt;/fields&gt;
- *     &lt;/table&gt;
- *     ...
- *   &lt;/tables&gt;
- * &lt;/database&gt;
- * </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 &quot;reference&quot;. 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.
      * &quot;Defined&quot; 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);
     }
 }



Mime
View raw message