You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by oh...@apache.org on 2006/04/16 19:29:11 UTC

svn commit: r394519 - in /jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree: NodeCombiner.java OverrideCombiner.java UnionCombiner.java ViewNode.java

Author: oheger
Date: Sun Apr 16 10:29:10 2006
New Revision: 394519

URL: http://svn.apache.org/viewcvs?rev=394519&view=rev
Log:
Added NodeCombiner classes

Added:
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java   (with props)
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java   (with props)
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java   (with props)
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java   (with props)

Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java?rev=394519&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java Sun Apr 16 10:29:10 2006
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.tree;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <p>
+ * A base class for node combiner implementations.
+ * </p>
+ * <p>
+ * A <em>node combiner</em> is an object that knows how two hierarchical node
+ * structures can be combined into a single one. Of course, there are many
+ * possible ways of implementing such a combination, e.g. constructing a union,
+ * an intersection, or an "override" structure (were nodes in the first
+ * hierarchy take precedence over nodes in the second hierarchy). This abstract
+ * base class only provides some helper methods and defines the common interface
+ * for node combiners. Concrete sub classes will implement the diverse
+ * combination algorithms.
+ * </p>
+ * <p>
+ * For some concrete combiner implementations it is important to distinguish
+ * whether a node is a single node or whether it belongs to a list structure.
+ * Alone from the input structures, the combiner will not always be able to make
+ * this decision. So sometimes it may be necessary for the developer to
+ * configure the combiner and tell it, which nodes should be treated as list
+ * nodes. For this purpose the <code>addListNode()</code> method exists. It
+ * can be passed the name of a node, which should be considered a list node.
+ * </p>
+ *
+ * @version $Id$
+ * @since 1.3
+ */
+public abstract class NodeCombiner
+{
+    /**
+     * Creates a new instance of <code>NodeCombiner</code>.
+     */
+    public NodeCombiner()
+    {
+        listNodes = new HashSet();
+    }
+
+    /** Stores a list with node names that are known to be list nodes. */
+    protected Set listNodes;
+
+    /**
+     * Adds the name of a node to the list of known list nodes. This means that
+     * nodes with this name will never be combined.
+     *
+     * @param nodeName the name to be added
+     */
+    public void addListNode(String nodeName)
+    {
+        listNodes.add(nodeName);
+    }
+
+    /**
+     * Returns a set with the names of nodes that are known to be list nodes.
+     *
+     * @return a set with the names of list nodes
+     */
+    public Set getListNodes()
+    {
+        return Collections.unmodifiableSet(listNodes);
+    }
+
+    /**
+     * Checks if a node is a list node. This implementation tests if the given
+     * node name is contained in the set of known list nodes. Derived classes
+     * which use different criteria may overload this method.
+     *
+     * @param node the node to be tested
+     * @return a flag whether this is a list node
+     */
+    public boolean isListNode(ConfigurationNode node)
+    {
+        return listNodes.contains(node.getName());
+    }
+
+    /**
+     * Combines the hierarchies represented by the given root nodes. This method
+     * must be defined in concrete sub classes with the implementation of a
+     * specific combination algorithm.
+     *
+     * @param node1 the first root node
+     * @param node2 the second root node
+     * @return the resulting combined node structure
+     */
+    public abstract ConfigurationNode combine(ConfigurationNode node1,
+            ConfigurationNode node2);
+
+    /**
+     * Creates a new view node. This method will be called whenever a new view
+     * node is to be created. It can be overriden to create special view nodes.
+     * This base implementation returns a new instance of
+     * <code>{@link ViewNode}</code>.
+     *
+     * @return the new view node
+     */
+    protected ViewNode createViewNode()
+    {
+        return new ViewNode();
+    }
+}

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java?rev=394519&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java Sun Apr 16 10:29:10 2006
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.tree;
+
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A concrete combiner implementation that is able to construct an override
+ * combination.
+ * </p>
+ * <p>
+ * An <em>override combination</em> means that nodes in the first node
+ * structure take precedence over nodes in the second, or - in other words -
+ * nodes of the second structure are only added to the resulting structure if
+ * they do not occure in the first one. This is especially suitable for dealing
+ * with the properties of configurations that are defined in an
+ * <code>override</code> section of a configuration definition file (hence the
+ * name).
+ * </p>
+ * <p>
+ * This combiner will iterate over the second node hierarchy and find all nodes
+ * that are not contained in the first hierarchy; these are added to the result.
+ * If a node can be found in both structures, it is checked whether a
+ * combination (in a recursive way) can be constructed for the two, which will
+ * then be added. Per default, nodes are combined, which occur only once in both
+ * structures. This test is implemented in the <code>canCombine()</code>
+ * method.
+ * </p>
+ * <p>
+ * As is true for the <code>{@link UnionCombiner}</code>, for this combiner
+ * list nodes are important. The <code>addListNode()</code> can be called to
+ * declare certain nodes as list nodes. This has the effect that these nodes
+ * will never be combined.
+ * </p>
+ *
+ * @version $Id$
+ * @since 1.3
+ */
+public class OverrideCombiner extends NodeCombiner
+{
+    /**
+     * Constructs an override combination for the passed in node structures.
+     *
+     * @param node1 the first node
+     * @param node2 the second node
+     * @return the resulting combined node structure
+     */
+    public ConfigurationNode combine(ConfigurationNode node1,
+            ConfigurationNode node2)
+    {
+        ViewNode result = createViewNode();
+        result.setName(node1.getName());
+
+        // Process nodes from the first structure, which override the second
+        for (Iterator it = node1.getChildren().iterator(); it.hasNext();)
+        {
+            ConfigurationNode child = (ConfigurationNode) it.next();
+            ConfigurationNode child2 = canCombine(node1, node2, child);
+            if (child2 != null)
+            {
+                result.addChild(combine(child, child2));
+            }
+            else
+            {
+                result.addChild(child);
+            }
+        }
+
+        // Process nodes from the second structure, which are not contained
+        // in the first structure
+        for (Iterator it = node2.getChildren().iterator(); it.hasNext();)
+        {
+            ConfigurationNode child = (ConfigurationNode) it.next();
+            if (node1.getChildrenCount(child.getName()) < 1)
+            {
+                result.addChild(child);
+            }
+        }
+
+        // Handle attributes and value
+        addAttributes(result, node1, node2);
+        result.setValue((node1.getValue() != null) ? node1.getValue() : node2
+                .getValue());
+
+        return result;
+    }
+
+    /**
+     * Handles the attributes during a combination process. First all attributes
+     * of the first node will be added to the result. Then all attributes of the
+     * second node, which are not contained in the first node, will also be
+     * added.
+     *
+     * @param result the resulting node
+     * @param node1 the first node
+     * @param node2 the second node
+     */
+    protected void addAttributes(ViewNode result, ConfigurationNode node1,
+            ConfigurationNode node2)
+    {
+        result.appendAttributes(node1);
+        for (Iterator it = node2.getAttributes().iterator(); it.hasNext();)
+        {
+            ConfigurationNode attr = (ConfigurationNode) it.next();
+            if (node1.getAttributeCount(attr.getName()) == 0)
+            {
+                result.addAttribute(attr);
+            }
+        }
+    }
+
+    /**
+     * Tests if a child node of the second node can be combined with the given
+     * child node of the first node. If this is the case, the corresponding node
+     * will be returned, otherwise <b>null</b>. This implementation checks
+     * whether the child node occurs only once in both hierarchies and is no
+     * known list node.
+     *
+     * @param node1 the first node
+     * @param node2 the second node
+     * @param child the child node (of the first node)
+     * @return a child of the second node, with which a combination is possible
+     */
+    protected ConfigurationNode canCombine(ConfigurationNode node1,
+            ConfigurationNode node2, ConfigurationNode child)
+    {
+        if (node2.getChildrenCount(child.getName()) == 1
+                && node1.getChildrenCount(child.getName()) == 1
+                && !isListNode(child))
+        {
+            return (ConfigurationNode) node2.getChildren(child.getName())
+                    .get(0);
+        }
+        else
+        {
+            return null;
+        }
+    }
+}

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/OverrideCombiner.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java?rev=394519&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java Sun Apr 16 10:29:10 2006
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.tree;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p>
+ * A specialized implementation of the <code>NodeCombiner</code> interface
+ * that constructs a union from two passed in node hierarchies.
+ * </p>
+ * <p>
+ * The given source hierarchies are traversed and their nodes are added to the
+ * resulting structure. Under some circumstances two nodes can be combined
+ * rather than adding both. This is the case if both nodes are single children
+ * (no lists) of their parents and do not have values. The corresponding check
+ * is implemented in the <code>findCombineNode()</code> method.
+ * </p>
+ * <p>
+ * Sometimes it is not possible for this combiner to detect whether two nodes
+ * can be combined or not. Consider the following two node hierarchies:
+ * </p>
+ * <p>
+ * 
+ * <pre>
+ * Hierarchy 1:
+ * 
+ * Database
+ *   +--Tables
+ *        +--Table
+ *             +--name [users]
+ *             +--fields
+ *                   +--field
+ *                   |    +--name [uid]
+ *                   +--field
+ *                   |    +--name [usrname]
+ *                     ...
+ * </pre>
+ * 
+ * </p>
+ * <p>
+ * 
+ * <pre>
+ * Hierarchy 2:
+ * 
+ * Database
+ *   +--Tables
+ *        +--Table
+ *             +--name [documents]
+ *             +--fields
+ *                   +--field
+ *                   |    +--name [docid]
+ *                   +--field
+ *                   |    +--name [docname]
+ *                     ...
+ * </pre>
+ * 
+ * </p>
+ * <p>
+ * Both hierarchies contain data about database tables. Each describes a single
+ * table. If these hierarchies are to be combined, the result should probably
+ * look like the following:
+ * <p>
+ * 
+ * <pre>
+ * Database
+ *   +--Tables
+ *        +--Table
+ *        |    +--name [users]
+ *        |    +--fields
+ *        |          +--field
+ *        |          |    +--name [uid]
+ *        |            ...
+ *        +--Table
+ *             +--name [documents]
+ *             +--fields
+ *                   +--field
+ *                   |    +--name [docid]
+ *                     ...
+ * </pre>
+ * 
+ * </p>
+ * <p>
+ * i.e. the <code>Tables</code> nodes should be combined, while the
+ * <code>Table</code> nodes should both be added to the resulting tree. From
+ * the combiner's point of view there is no difference between the
+ * <code>Tables</code> and the <code>Table</code> nodes in the source trees,
+ * so the developer has to help out and give a hint that the <code>Table</code>
+ * nodes belong to a list structure. This can be done using the
+ * <code>addListNode()</code> method; this method expects the name of a node,
+ * which should be treated as a list node. So if
+ * <code>addListNode("Table");</code> was called, the combiner knows that it
+ * must not combine the <code>Table</code> nodes, but add it both to the
+ * resulting tree.
+ * </p>
+ * 
+ * @version $Id$
+ * @since 1.3
+ */
+public class UnionCombiner extends NodeCombiner
+{
+    /**
+     * Combines the given nodes to a new union node.
+     * 
+     * @param node1 the first source node
+     * @param node2 the second source node
+     * @return the union node
+     */
+    public ConfigurationNode combine(ConfigurationNode node1,
+            ConfigurationNode node2)
+    {
+        ViewNode result = createViewNode();
+        result.setName(node1.getName());
+        result.appendAttributes(node1);
+        result.appendAttributes(node2);
+
+        // Check if nodes can be combined
+        List children2 = new LinkedList(node2.getChildren());
+        for (Iterator it = node1.getChildren().iterator(); it.hasNext();)
+        {
+            ConfigurationNode child1 = (ConfigurationNode) it.next();
+            ConfigurationNode child2 = findCombineNode(node1, node2, child1,
+                    children2);
+            if (child2 != null)
+            {
+                result.addChild(combine(child1, child2));
+                children2.remove(child2);
+            }
+            else
+            {
+                result.addChild(child1);
+            }
+        }
+
+        // Add remaining children of node 2
+        for (Iterator it = children2.iterator(); it.hasNext();)
+        {
+            result.addChild((ConfigurationNode) it.next());
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * Tries to find a child node of the second source node, with whitch a child
+     * of the first source node can be combined. During combining of the source
+     * nodes an iteration over the first source node's children is performed.
+     * For each child node it is checked whether a corresponding child node in
+     * the second source node exists. If this is the case, these corresponsing
+     * child nodes are recursively combined and the result is added to the
+     * combined node. This method implements the checks whether such a recursive
+     * combination is possible. The actual implementation tests the following
+     * conditions:
+     * </p>
+     * <p>
+     * <ul>
+     * <li>In both the first and the second source node there is only one child
+     * node with the given name (no list structures).</li>
+     * <li>The given name is not in the list of known list nodes, i.e. it was
+     * not passed to the <code>addListNode()</code> method.</li>
+     * <li>None of these matching child nodes has a value.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * If all of these tests are successfull, the matching child node of the
+     * second source node is returned. Otherwise the result is <b>null</b>.
+     * </p>
+     * 
+     * @param node1 the first source node
+     * @param node2 the second source node
+     * @param child the child node of the first source node to be checked
+     * @param children a list with all children of the second source node
+     * @return the matching child node of the second source node or <b>null</b>
+     * if there is none
+     */
+    protected ConfigurationNode findCombineNode(ConfigurationNode node1,
+            ConfigurationNode node2, ConfigurationNode child, List children)
+    {
+        if (child.getValue() == null && !isListNode(child)
+                && node1.getChildrenCount(child.getName()) == 1
+                && node2.getChildrenCount(child.getName()) == 1)
+        {
+            ConfigurationNode child2 = (ConfigurationNode) node2.getChildren(
+                    child.getName()).iterator().next();
+            if (child2.getValue() == null)
+            {
+                return child2;
+            }
+        }
+        return null;
+    }
+}

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/UnionCombiner.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java?rev=394519&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java (added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java Sun Apr 16 10:29:10 2006
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.tree;
+
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A specialized node implementation to be used in view configurations.
+ * </p>
+ * <p>
+ * Some configurations provide a logical view on the nodes of other
+ * configurations. These configurations construct their own hierarchy of nodes
+ * based on the node trees of their source configurations. This special node
+ * class can be used for this purpose. It allows child nodes and attributes to
+ * be added without changing their parent node. So a node can belong to a
+ * hierarchy of nodes of a source configuration, but be also contained in a view
+ * configuration.
+ * </p>
+ *
+ * @version $Id$
+ * @since 1.3
+ */
+public class ViewNode extends DefaultConfigurationNode
+{
+    /**
+     * Adds an attribute to this view node. The new attribute's parent node will
+     * be saved.
+     *
+     * @param attr the attribute node to be added
+     */
+    public void addAttribute(ConfigurationNode attr)
+    {
+        ConfigurationNode parent = null;
+
+        if (attr != null)
+        {
+            parent = attr.getParentNode();
+        }
+        super.addAttribute(attr);
+        attr.setParentNode(parent);
+    }
+
+    /**
+     * Adds a child node to this view node. The new child's parent node will be
+     * saved.
+     *
+     * @param child the child node to be added
+     */
+    public void addChild(ConfigurationNode child)
+    {
+        ConfigurationNode parent = null;
+
+        if (child != null)
+        {
+            parent = child.getParentNode();
+        }
+        super.addChild(child);
+        child.setParentNode(parent);
+    }
+
+    /**
+     * Adds all attribute nodes of the given source node to this view node.
+     *
+     * @param source the source node
+     */
+    public void appendAttributes(ConfigurationNode source)
+    {
+        if (source != null)
+        {
+            for (Iterator it = source.getAttributes().iterator(); it.hasNext();)
+            {
+                addAttribute((ConfigurationNode) it.next());
+            }
+        }
+    }
+
+    /**
+     * Adds all child nodes of the given source node to this view node.
+     *
+     * @param source the source node
+     */
+    public void appendChildren(ConfigurationNode source)
+    {
+        if (source != null)
+        {
+            for (Iterator it = source.getChildren().iterator(); it.hasNext();)
+            {
+                addChild((ConfigurationNode) it.next());
+            }
+        }
+    }
+}

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ViewNode.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org