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