001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.configuration.tree;
018    
019    
020    /**
021     * <p>
022     * A concrete combiner implementation that is able to construct an override
023     * combination.
024     * </p>
025     * <p>
026     * An <em>override combination</em> means that nodes in the first node
027     * structure take precedence over nodes in the second, or - in other words -
028     * nodes of the second structure are only added to the resulting structure if
029     * they do not occur in the first one. This is especially suitable for dealing
030     * with the properties of configurations that are defined in an
031     * {@code override} section of a configuration definition file (hence the
032     * name).
033     * </p>
034     * <p>
035     * This combiner will iterate over the second node hierarchy and find all nodes
036     * that are not contained in the first hierarchy; these are added to the result.
037     * If a node can be found in both structures, it is checked whether a
038     * combination (in a recursive way) can be constructed for the two, which will
039     * then be added. Per default, nodes are combined, which occur only once in both
040     * structures. This test is implemented in the {@code canCombine()}
041     * method.
042     * </p>
043     * <p>
044     * As is true for the {@link UnionCombiner}, for this combiner
045     * list nodes are important. The {@code addListNode()} can be called to
046     * declare certain nodes as list nodes. This has the effect that these nodes
047     * will never be combined.
048     * </p>
049     *
050     * @author <a
051     * href="http://commons.apache.org/configuration/team-list.html">Commons
052     * Configuration team</a>
053     * @version $Id: OverrideCombiner.java 1206482 2011-11-26 16:34:01Z oheger $
054     * @since 1.3
055     */
056    public class OverrideCombiner extends NodeCombiner
057    {
058        /**
059         * Constructs an override combination for the passed in node structures.
060         *
061         * @param node1 the first node
062         * @param node2 the second node
063         * @return the resulting combined node structure
064         */
065        @Override
066        public ConfigurationNode combine(ConfigurationNode node1,
067                ConfigurationNode node2)
068        {
069            ViewNode result = createViewNode();
070            result.setName(node1.getName());
071    
072            // Process nodes from the first structure, which override the second
073            for (ConfigurationNode child : node1.getChildren())
074            {
075                ConfigurationNode child2 = canCombine(node1, node2, child);
076                if (child2 != null)
077                {
078                    result.addChild(combine(child, child2));
079                }
080                else
081                {
082                    result.addChild(child);
083                }
084            }
085    
086            // Process nodes from the second structure, which are not contained
087            // in the first structure
088            for (ConfigurationNode child : node2.getChildren())
089            {
090                if (node1.getChildrenCount(child.getName()) < 1)
091                {
092                    result.addChild(child);
093                }
094            }
095    
096            // Handle attributes and value
097            addAttributes(result, node1, node2);
098            result.setValue((node1.getValue() != null) ? node1.getValue() : node2
099                    .getValue());
100    
101            return result;
102        }
103    
104        /**
105         * Handles the attributes during a combination process. First all attributes
106         * of the first node will be added to the result. Then all attributes of the
107         * second node, which are not contained in the first node, will also be
108         * added.
109         *
110         * @param result the resulting node
111         * @param node1 the first node
112         * @param node2 the second node
113         */
114        protected void addAttributes(ViewNode result, ConfigurationNode node1,
115                ConfigurationNode node2)
116        {
117            result.appendAttributes(node1);
118            for (ConfigurationNode attr : node2.getAttributes())
119            {
120                if (node1.getAttributeCount(attr.getName()) == 0)
121                {
122                    result.addAttribute(attr);
123                }
124            }
125        }
126    
127        /**
128         * Tests if a child node of the second node can be combined with the given
129         * child node of the first node. If this is the case, the corresponding node
130         * will be returned, otherwise <b>null</b>. This implementation checks
131         * whether the child node occurs only once in both hierarchies and is no
132         * known list node.
133         *
134         * @param node1 the first node
135         * @param node2 the second node
136         * @param child the child node (of the first node)
137         * @return a child of the second node, with which a combination is possible
138         */
139        protected ConfigurationNode canCombine(ConfigurationNode node1,
140                ConfigurationNode node2, ConfigurationNode child)
141        {
142            if (node2.getChildrenCount(child.getName()) == 1
143                    && node1.getChildrenCount(child.getName()) == 1
144                    && !isListNode(child))
145            {
146                return (ConfigurationNode) node2.getChildren(child.getName())
147                        .get(0);
148            }
149            else
150            {
151                return null;
152            }
153        }
154    }