1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.util.xml;
24  
25  import com.liferay.util.xml.descriptor.XMLDescriptor;
26  
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Vector;
32  
33  import org.dom4j.Document;
34  import org.dom4j.Element;
35  
36  /**
37   * <a href="XMLMerger.java.html"><b><i>View Source</i></b></a>
38   *
39   * @author Brian Wing Shun Chan
40   * @author Alan Zimmerman
41   * @author Jorge Ferrer
42   *
43   */
44  public class XMLMerger {
45  
46      public XMLMerger(XMLDescriptor descriptor) {
47          _descriptor = descriptor;
48      }
49  
50      public XMLElementComparator getElementComparator() {
51          return new XMLElementComparator(_descriptor);
52      }
53  
54      public Document merge(Document masterDoc, Document slaveDoc) {
55          Document mergedDoc = (Document)masterDoc.clone();
56  
57          Element root1 = mergedDoc.getRootElement();
58          Element root2 = slaveDoc.getRootElement();
59  
60          List<Element> children = root2.elements();
61  
62          for (Element el2 : children) {
63              Element el2Clone = (Element)el2.clone();
64  
65              el2Clone.detach();
66  
67              root1.add(el2Clone);
68          }
69  
70          organizeXML(mergedDoc);
71  
72          return mergedDoc;
73      }
74  
75      public void organizeXML(Document doc) {
76          Element root = doc.getRootElement();
77  
78          _orderChildren(root, _descriptor.getRootChildrenOrder());
79          _mergeDuplicateElements(root, getElementComparator());
80      }
81  
82      private void _addChildren(
83          Element first, Collection<Element> childrenToJoin) {
84  
85          Collection<Element> clones = new Vector<Element>();
86  
87          Iterator<Element> itr = childrenToJoin.iterator();
88  
89          while (itr.hasNext()) {
90              clones.add((Element)itr.next().clone());
91          }
92  
93          first.elements().addAll(clones);
94  
95          _orderChildren(first, _descriptor.getChildrenOrder(first));
96      }
97  
98      private boolean _containsObjectEqualTo(
99          Element example, List<Element> list, ElementComparator comparator) {
100 
101         Iterator<Element> itr = list.iterator();
102 
103         while (itr.hasNext()) {
104             Element candidate = itr.next();
105 
106             if (comparator.compare(example, candidate) == 0) {
107                 return true;
108             }
109         }
110 
111         return false;
112     }
113 
114     private Element _findObjectEqualTo(
115         Element example, List<Element> list, ElementComparator comparator) {
116 
117         Iterator<Element> itr = list.iterator();
118 
119         while (itr.hasNext()) {
120             Element candidate = itr.next();
121 
122             if (comparator.compare(example, candidate) == 0) {
123                 return candidate;
124             }
125         }
126 
127         return example;
128     }
129 
130     private void _mergeDuplicateElements(
131         Element el, ElementComparator comparator) {
132 
133         if (el.elements().size() > 0) {
134             List<Element> children = el.elements();
135 
136             List<Element> originals = new ArrayList<Element>();
137             List<Element> duplicates = new ArrayList<Element>();
138 
139             for (int i = 0; i < children.size(); i++) {
140                 Element child = children.get(i);
141 
142                 if (_containsObjectEqualTo(child, originals, comparator)) {
143                     if (_descriptor.canJoinChildren(child)) {
144                         Element first =
145                             _findObjectEqualTo(child, originals, comparator);
146 
147                         Collection<Element> childrenToJoin = child.elements();
148 
149                         _addChildren(first, childrenToJoin);
150                     }
151 
152                     duplicates.add(child);
153                 }
154                 else {
155                     originals.add(child);
156                 }
157             }
158 
159             for (Element duplicate : duplicates) {
160                 duplicate.detach();
161             }
162 
163             Iterator<Element> itr = originals.iterator();
164 
165             while (itr.hasNext()) {
166                 Element child = itr.next();
167 
168                 _mergeDuplicateElements(child, comparator);
169             }
170         }
171     }
172 
173     private void _orderChildren(
174         Element parent, String[] orderedChildrenNames) {
175 
176         if (orderedChildrenNames == null) {
177             return;
178         }
179 
180         List<Element> elements = new ArrayList<Element>();
181 
182         for (int i = 0; i < orderedChildrenNames.length; i++) {
183             elements.addAll(parent.elements(orderedChildrenNames[i]));
184         }
185 
186         for (Element el : elements) {
187             el.detach();
188 
189             parent.add(el);
190         }
191     }
192 
193     private XMLDescriptor _descriptor;
194 
195 }