001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.util.xml;
016    
017    import com.liferay.util.xml.descriptor.XMLDescriptor;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Vector;
024    
025    import org.dom4j.Document;
026    import org.dom4j.Element;
027    
028    /**
029     * @author Brian Wing Shun Chan
030     * @author Alan Zimmerman
031     * @author Jorge Ferrer
032     */
033    public class XMLMerger {
034    
035            public XMLMerger(XMLDescriptor descriptor) {
036                    _descriptor = descriptor;
037            }
038    
039            public XMLElementComparator getElementComparator() {
040                    return new XMLElementComparator(_descriptor);
041            }
042    
043            public Document merge(Document masterDoc, Document slaveDoc) {
044                    Document mergedDoc = (Document)masterDoc.clone();
045    
046                    Element root1 = mergedDoc.getRootElement();
047                    Element root2 = slaveDoc.getRootElement();
048    
049                    List<Element> children = root2.elements();
050    
051                    for (Element el2 : children) {
052                            Element el2Clone = (Element)el2.clone();
053    
054                            el2Clone.detach();
055    
056                            root1.add(el2Clone);
057                    }
058    
059                    organizeXML(mergedDoc);
060    
061                    return mergedDoc;
062            }
063    
064            public void organizeXML(Document doc) {
065                    Element root = doc.getRootElement();
066    
067                    _orderChildren(root, _descriptor.getRootChildrenOrder());
068                    _mergeDuplicateElements(root, getElementComparator());
069            }
070    
071            private void _addChildren(
072                    Element first, Collection<Element> childrenToJoin) {
073    
074                    Collection<Element> clones = new Vector<Element>();
075    
076                    Iterator<Element> itr = childrenToJoin.iterator();
077    
078                    while (itr.hasNext()) {
079                            clones.add((Element)itr.next().clone());
080                    }
081    
082                    first.elements().addAll(clones);
083    
084                    _orderChildren(first, _descriptor.getChildrenOrder(first));
085            }
086    
087            private boolean _containsObjectEqualTo(
088                    Element example, List<Element> list, ElementComparator comparator) {
089    
090                    Iterator<Element> itr = list.iterator();
091    
092                    while (itr.hasNext()) {
093                            Element candidate = itr.next();
094    
095                            if (comparator.compare(example, candidate) == 0) {
096                                    return true;
097                            }
098                    }
099    
100                    return false;
101            }
102    
103            private Element _findObjectEqualTo(
104                    Element example, List<Element> list, ElementComparator comparator) {
105    
106                    Iterator<Element> itr = list.iterator();
107    
108                    while (itr.hasNext()) {
109                            Element candidate = itr.next();
110    
111                            if (comparator.compare(example, candidate) == 0) {
112                                    return candidate;
113                            }
114                    }
115    
116                    return example;
117            }
118    
119            private void _mergeDuplicateElements(
120                    Element el, ElementComparator comparator) {
121    
122                    if (el.elements().size() > 0) {
123                            List<Element> children = el.elements();
124    
125                            List<Element> originals = new ArrayList<Element>();
126                            List<Element> duplicates = new ArrayList<Element>();
127    
128                            for (int i = 0; i < children.size(); i++) {
129                                    Element child = children.get(i);
130    
131                                    if (_containsObjectEqualTo(child, originals, comparator)) {
132                                            if (_descriptor.canJoinChildren(child)) {
133                                                    Element first =
134                                                            _findObjectEqualTo(child, originals, comparator);
135    
136                                                    Collection<Element> childrenToJoin = child.elements();
137    
138                                                    _addChildren(first, childrenToJoin);
139                                            }
140    
141                                            duplicates.add(child);
142                                    }
143                                    else {
144                                            originals.add(child);
145                                    }
146                            }
147    
148                            for (Element duplicate : duplicates) {
149                                    duplicate.detach();
150                            }
151    
152                            Iterator<Element> itr = originals.iterator();
153    
154                            while (itr.hasNext()) {
155                                    Element child = itr.next();
156    
157                                    _mergeDuplicateElements(child, comparator);
158                            }
159                    }
160            }
161    
162            private void _orderChildren(
163                    Element parent, String[] orderedChildrenNames) {
164    
165                    if (orderedChildrenNames == null) {
166                            return;
167                    }
168    
169                    List<Element> elements = new ArrayList<Element>();
170    
171                    for (int i = 0; i < orderedChildrenNames.length; i++) {
172                            elements.addAll(parent.elements(orderedChildrenNames[i]));
173                    }
174    
175                    for (Element el : elements) {
176                            el.detach();
177    
178                            parent.add(el);
179                    }
180            }
181    
182            private XMLDescriptor _descriptor;
183    
184    }