001    /**
002     * Copyright (c) 2000-2013 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.List;
022    
023    import org.dom4j.Document;
024    import org.dom4j.Element;
025    
026    /**
027     * @author Brian Wing Shun Chan
028     * @author Alan Zimmerman
029     * @author Jorge Ferrer
030     */
031    public class XMLMerger {
032    
033            public XMLMerger(XMLDescriptor descriptor) {
034                    _descriptor = descriptor;
035            }
036    
037            public XMLElementComparator getElementComparator() {
038                    return new XMLElementComparator(_descriptor);
039            }
040    
041            public Document merge(Document masterDocument, Document slaveDocument) {
042                    Document mergedDocument = (Document)masterDocument.clone();
043    
044                    Element mergedRootElement = mergedDocument.getRootElement();
045                    Element slaveRootElement = slaveDocument.getRootElement();
046    
047                    for (Element slaveElement :
048                                    (List<Element>)slaveRootElement.elements()) {
049    
050                            Element clonedSlaveElement = (Element)slaveElement.clone();
051    
052                            clonedSlaveElement.detach();
053    
054                            mergedRootElement.add(clonedSlaveElement);
055                    }
056    
057                    organizeXML(mergedDocument);
058    
059                    return mergedDocument;
060            }
061    
062            public void organizeXML(Document document) {
063                    Element rootElement = document.getRootElement();
064    
065                    _orderChildren(rootElement, _descriptor.getRootChildrenOrder());
066                    _mergeDuplicateElements(rootElement, getElementComparator());
067            }
068    
069            private void _addChildren(
070                    Element firstElement, Collection<Element> childElements) {
071    
072                    List<Element> elements = firstElement.elements();
073    
074                    for (Element childElement : childElements) {
075                            elements.add((Element)childElement.clone());
076                    }
077    
078                    _orderChildren(
079                            firstElement, _descriptor.getChildrenOrder(firstElement));
080            }
081    
082            private boolean _containsObjectEqualTo(
083                    Element element, List<Element> elements,
084                    ElementComparator elementComparator) {
085    
086                    for (Element curElement : elements) {
087                            if (elementComparator.compare(element, curElement) == 0) {
088                                    return true;
089                            }
090                    }
091    
092                    return false;
093            }
094    
095            private Element _findObjectEqualTo(
096                    Element element, List<Element> elements,
097                    ElementComparator elementComparator) {
098    
099                    for (Element curElement : elements) {
100                            if (elementComparator.compare(element, curElement) == 0) {
101                                    return curElement;
102                            }
103                    }
104    
105                    return element;
106            }
107    
108            private void _mergeDuplicateElements(
109                    Element element, ElementComparator elementComparator) {
110    
111                    List<Element> childElements = element.elements();
112    
113                    if (childElements.isEmpty()) {
114                            return;
115                    }
116    
117                    List<Element> originalElements = new ArrayList<Element>();
118                    List<Element> duplicateElements = new ArrayList<Element>();
119    
120                    for (int i = 0; i < childElements.size(); i++) {
121                            Element childElement = childElements.get(i);
122    
123                            if (_containsObjectEqualTo(
124                                            childElement, originalElements, elementComparator)) {
125    
126                                    if (_descriptor.canJoinChildren(childElement)) {
127                                            Element firstElement = _findObjectEqualTo(
128                                                    childElement, originalElements, elementComparator);
129    
130                                            _addChildren(firstElement, childElement.elements());
131                                    }
132    
133                                    duplicateElements.add(childElement);
134                            }
135                            else {
136                                    originalElements.add(childElement);
137                            }
138    
139                            _orderChildren(
140                                    childElement, _descriptor.getChildrenOrder(childElement));
141                    }
142    
143                    for (Element duplicateElement : duplicateElements) {
144                            duplicateElement.detach();
145                    }
146    
147                    for (Element childElement : originalElements) {
148                            _mergeDuplicateElements(childElement, elementComparator);
149                    }
150            }
151    
152            private void _orderChildren(
153                    Element parentElement, String[] orderedChildrenNames) {
154    
155                    if (orderedChildrenNames == null) {
156                            return;
157                    }
158    
159                    List<Element> elements = new ArrayList<Element>();
160    
161                    for (int i = 0; i < orderedChildrenNames.length; i++) {
162                            elements.addAll(parentElement.elements(orderedChildrenNames[i]));
163                    }
164    
165                    for (Element element : elements) {
166                            element.detach();
167    
168                            parentElement.add(element);
169                    }
170            }
171    
172            private XMLDescriptor _descriptor;
173    
174    }