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>[EMAIL PROTECTED] 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>[EMAIL PROTECTED] 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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]