1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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  public class XMLMerger {
44  
45      public XMLMerger(XMLDescriptor descriptor) {
46          _descriptor = descriptor;
47      }
48  
49      public XMLElementComparator getElementComparator() {
50          return new XMLElementComparator(_descriptor);
51      }
52  
53      public Document merge(Document masterDoc, Document slaveDoc) {
54          Document mergedDoc = (Document)masterDoc.clone();
55  
56          Element root1 = mergedDoc.getRootElement();
57          Element root2 = slaveDoc.getRootElement();
58  
59          List<Element> children = root2.elements();
60  
61          for (Element el2 : children) {
62              Element el2Clone = (Element)el2.clone();
63  
64              el2Clone.detach();
65  
66              root1.add(el2Clone);
67          }
68  
69          organizeXML(mergedDoc);
70  
71          return mergedDoc;
72      }
73  
74      public void organizeXML(Document doc) {
75          Element root = doc.getRootElement();
76  
77          _orderChildren(root, _descriptor.getRootChildrenOrder());
78          _mergeDuplicateElements(root, getElementComparator());
79      }
80  
81      private void _addChildren(
82          Element first, Collection<Element> childrenToJoin) {
83  
84          Collection<Element> clones = new Vector<Element>();
85  
86          Iterator<Element> itr = childrenToJoin.iterator();
87  
88          while (itr.hasNext()) {
89              clones.add((Element)itr.next().clone());
90          }
91  
92          first.elements().addAll(clones);
93  
94          _orderChildren(first, _descriptor.getChildrenOrder(first));
95      }
96  
97      private boolean _containsObjectEqualTo(
98          Element example, List<Element> list, ElementComparator comparator) {
99  
100         Iterator<Element> itr = list.iterator();
101 
102         while (itr.hasNext()) {
103             Element candidate = itr.next();
104 
105             if (comparator.compare(example, candidate) == 0) {
106                 return true;
107             }
108         }
109 
110         return false;
111     }
112 
113     private Element _findObjectEqualTo(
114         Element example, List<Element> list, ElementComparator comparator) {
115 
116         Iterator<Element> itr = list.iterator();
117 
118         while (itr.hasNext()) {
119             Element candidate = itr.next();
120 
121             if (comparator.compare(example, candidate) == 0) {
122                 return candidate;
123             }
124         }
125 
126         return example;
127     }
128 
129     private void _mergeDuplicateElements(
130         Element el, ElementComparator comparator) {
131 
132         if (el.elements().size() > 0) {
133             List<Element> children = el.elements();
134 
135             List<Element> originals = new ArrayList<Element>();
136             List<Element> duplicates = new ArrayList<Element>();
137 
138             for (int i = 0; i < children.size(); i++) {
139                 Element child = children.get(i);
140 
141                 if (_containsObjectEqualTo(child, originals, comparator)) {
142                     if (_descriptor.canJoinChildren(child)) {
143                         Element first =
144                             _findObjectEqualTo(child, originals, comparator);
145 
146                         Collection<Element> childrenToJoin = child.elements();
147 
148                         _addChildren(first, childrenToJoin);
149                     }
150 
151                     duplicates.add(child);
152                 }
153                 else {
154                     originals.add(child);
155                 }
156             }
157 
158             for (Element duplicate : duplicates) {
159                 duplicate.detach();
160             }
161 
162             Iterator<Element> itr = originals.iterator();
163 
164             while (itr.hasNext()) {
165                 Element child = itr.next();
166 
167                 _mergeDuplicateElements(child, comparator);
168             }
169         }
170     }
171 
172     private void _orderChildren(
173         Element parent, String[] orderedChildrenNames) {
174 
175         if (orderedChildrenNames == null) {
176             return;
177         }
178 
179         List<Element> elements = new ArrayList<Element>();
180 
181         for (int i = 0; i < orderedChildrenNames.length; i++) {
182             elements.addAll(parent.elements(orderedChildrenNames[i]));
183         }
184 
185         for (Element el : elements) {
186             el.detach();
187 
188             parent.add(el);
189         }
190     }
191 
192     private XMLDescriptor _descriptor;
193 
194 }