View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration.tree;
18  
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.LinkedList;
22  import java.util.List;
23  
24  /**
25   * <p>
26   * A specialized implementation of the {@code NodeCombiner} interface
27   * that performs a merge from two passed in node hierarchies.
28   * </p>
29   * <p>
30   * This combiner performs the merge using a few rules:
31   * <ol>
32   * <li>Nodes can be merged when attributes that appear in both have the same value.</li>
33   * <li>Only a single node in the second file is considered a match to the node in the first file.</li>
34   * <li>Attributes in nodes that match are merged.
35   * <li>Nodes in both files that do not match are added to the result.</li>
36   * </ol>
37   * </p>
38   *
39   * @author <a
40   * href="http://commons.apache.org/configuration/team-list.html">Commons
41   * Configuration team</a>
42   * @version $Id: MergeCombiner.java 1301991 2012-03-17 20:18:02Z sebb $
43   * @since 1.7
44   */
45  public class MergeCombiner extends NodeCombiner
46  {
47      /**
48       * Combines the given nodes to a new union node.
49       *
50       * @param node1 the first source node
51       * @param node2 the second source node
52       * @return the union node
53       */
54  
55      @Override
56      public ConfigurationNode combine(ConfigurationNode node1, ConfigurationNode node2)
57      {
58          ViewNode result = createViewNode();
59          result.setName(node1.getName());
60          result.setValue(node1.getValue());
61          addAttributes(result, node1, node2);
62  
63          // Check if nodes can be combined
64          List<ConfigurationNode> children2 = new LinkedList<ConfigurationNode>(node2.getChildren());
65          for (ConfigurationNode child1 : node1.getChildren())
66          {
67              ConfigurationNode child2 = canCombine(node1, node2, child1, children2);
68              if (child2 != null)
69              {
70                  result.addChild(combine(child1, child2));
71                  children2.remove(child2);
72              }
73              else
74              {
75                  result.addChild(child1);
76              }
77          }
78  
79          // Add remaining children of node 2
80          for (ConfigurationNode c : children2)
81          {
82              result.addChild(c);
83          }
84          return result;
85      }
86  
87      /**
88       * Handles the attributes during a combination process. First all attributes
89       * of the first node will be added to the result. Then all attributes of the
90       * second node, which are not contained in the first node, will also be
91       * added.
92       *
93       * @param result the resulting node
94       * @param node1 the first node
95       * @param node2 the second node
96       */
97      protected void addAttributes(ViewNode result, ConfigurationNode node1,
98              ConfigurationNode node2)
99      {
100         result.appendAttributes(node1);
101         for (ConfigurationNode attr : node2.getAttributes())
102         {
103             if (node1.getAttributeCount(attr.getName()) == 0)
104             {
105                 result.addAttribute(attr);
106             }
107         }
108     }
109 
110     /**
111      * Tests if the first node can be combined with the second node. A node can
112      * only be combined if its attributes are all present in the second node and
113      * they all have the same value.
114      *
115      * @param node1 the first node
116      * @param node2 the second node
117      * @param child the child node (of the first node)
118      * @return a child of the second node, with which a combination is possible
119      */
120     protected ConfigurationNode canCombine(ConfigurationNode node1,
121             ConfigurationNode node2, ConfigurationNode child, List<ConfigurationNode> children2)
122     {
123         List<ConfigurationNode> attrs1 = child.getAttributes();
124         List<ConfigurationNode> nodes = new ArrayList<ConfigurationNode>();
125 
126         List<ConfigurationNode> children = node2.getChildren(child.getName());
127         Iterator<ConfigurationNode> it = children.iterator();
128         while (it.hasNext())
129         {
130             ConfigurationNode node = it.next();
131             Iterator<ConfigurationNode> iter = attrs1.iterator();
132             while (iter.hasNext())
133             {
134                 ConfigurationNode attr1 = iter.next();
135                 List<ConfigurationNode> list2 = node.getAttributes(attr1.getName());
136                 if (list2.size() == 1
137                     && !attr1.getValue().equals(list2.get(0).getValue()))
138                 {
139                     node = null;
140                     break;
141                 }
142             }
143             if (node != null)
144             {
145                 nodes.add(node);
146             }
147         }
148 
149         if (nodes.size() == 1)
150         {
151             return nodes.get(0);
152         }
153         if (nodes.size() > 1 && !isListNode(child))
154         {
155             Iterator<ConfigurationNode> iter = nodes.iterator();
156             while (iter.hasNext())
157             {
158                 children2.remove(iter.next());
159             }
160         }
161 
162         return null;
163     }
164 }