001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.comparison;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.types.CmsResourceTypeXmlPage;
033import org.opencms.main.CmsException;
034import org.opencms.xml.I_CmsXmlDocument;
035import org.opencms.xml.content.CmsXmlContent;
036import org.opencms.xml.content.CmsXmlContentFactory;
037import org.opencms.xml.content.I_CmsXmlContentValueVisitor;
038import org.opencms.xml.page.CmsXmlPageFactory;
039import org.opencms.xml.types.I_CmsXmlContentValue;
040
041import java.util.ArrayList;
042import java.util.Collections;
043import java.util.Iterator;
044import java.util.List;
045import java.util.Locale;
046
047/**
048 * A comparison of properties, attributes and elements of xml documents.<p>
049 */
050public class CmsXmlDocumentComparison extends CmsResourceComparison {
051
052    /**
053     * Visitor that collects the xpath expressions of xml contents.<p>
054     */
055    static class CmsXmlContentElementPathExtractor implements I_CmsXmlContentValueVisitor {
056
057        /** The paths to the elements in the xml content. */
058        private List<CmsElementComparison> m_elementPaths;
059
060        /**
061         * Creates a CmsXmlContentElementPathExtractor.<p>
062         */
063        CmsXmlContentElementPathExtractor() {
064
065            m_elementPaths = new ArrayList<CmsElementComparison>();
066        }
067
068        /**
069         *
070         * @see org.opencms.xml.content.I_CmsXmlContentValueVisitor#visit(org.opencms.xml.types.I_CmsXmlContentValue)
071         */
072        public void visit(I_CmsXmlContentValue value) {
073
074            // only add simple types
075            if (value.isSimpleType()) {
076                m_elementPaths.add(
077                    new CmsXmlContentElementComparison(value.getLocale(), value.getPath(), value.getTypeName()));
078            }
079        }
080
081        /**
082         * Returns the elementPaths.<p>
083         *
084         * @return the elementPaths
085         */
086        List<CmsElementComparison> getElementPaths() {
087
088            return m_elementPaths;
089        }
090    }
091
092    /** The compared elements.<p> */
093    private List<CmsElementComparison> m_elements;
094
095    /**
096     * Creates a new xml document comparison.<p>
097     *
098     * @param cms the CmsObject to use
099     * @param res1 the first file to compare
100     * @param res2 the second file to compare
101     *
102     * @throws CmsException if something goes wrong
103     */
104    public CmsXmlDocumentComparison(CmsObject cms, CmsFile res1, CmsFile res2)
105    throws CmsException {
106
107        I_CmsXmlDocument resource1;
108        I_CmsXmlDocument resource2;
109
110        List<CmsElementComparison> elements1 = null;
111        List<CmsElementComparison> elements2 = null;
112
113        if (CmsResourceTypeXmlPage.isXmlPage(res1) && CmsResourceTypeXmlPage.isXmlPage(res2)) {
114            resource1 = CmsXmlPageFactory.unmarshal(cms, res1);
115            resource2 = CmsXmlPageFactory.unmarshal(cms, res2);
116            elements1 = getElements(resource1);
117            elements2 = getElements(resource2);
118        } else {
119            resource1 = CmsXmlContentFactory.unmarshal(cms, res1);
120            CmsXmlContentElementPathExtractor visitor = new CmsXmlContentElementPathExtractor();
121            ((CmsXmlContent)resource1).visitAllValuesWith(visitor);
122            elements1 = visitor.getElementPaths();
123            resource2 = CmsXmlContentFactory.unmarshal(cms, res2);
124            visitor = new CmsXmlContentElementPathExtractor();
125            ((CmsXmlContent)resource2).visitAllValuesWith(visitor);
126            elements2 = visitor.getElementPaths();
127        }
128
129        List<CmsElementComparison> removed = new ArrayList<CmsElementComparison>(elements1);
130        removed.removeAll(elements2);
131        Iterator<CmsElementComparison> i = removed.iterator();
132        while (i.hasNext()) {
133            CmsElementComparison elem = i.next();
134            elem.setStatus(CmsResourceComparison.TYPE_REMOVED);
135            String value = resource1.getValue(elem.getName(), elem.getLocale()).getStringValue(cms);
136            elem.setVersion1(value);
137            elem.setVersion2("");
138        }
139        List<CmsElementComparison> added = new ArrayList<CmsElementComparison>(elements2);
140        added.removeAll(elements1);
141        i = added.iterator();
142        while (i.hasNext()) {
143            CmsElementComparison elem = i.next();
144            elem.setStatus(CmsResourceComparison.TYPE_ADDED);
145            elem.setVersion1("");
146            I_CmsXmlContentValue contentValue = resource2.getValue(elem.getName(), elem.getLocale());
147            String value = contentValue.getStringValue(cms);
148            elem.setVersion2(value);
149        }
150        List<CmsElementComparison> union = new ArrayList<CmsElementComparison>(elements1);
151        union.retainAll(elements2);
152
153        // find out, which elements were changed
154        i = new ArrayList<CmsElementComparison>(union).iterator();
155        while (i.hasNext()) {
156            CmsElementComparison elem = i.next();
157            String value1 = resource1.getValue(elem.getName(), elem.getLocale()).getStringValue(cms);
158            String value2 = resource2.getValue(elem.getName(), elem.getLocale()).getStringValue(cms);
159            if (value1 == null) {
160                value1 = "";
161            }
162            if (value2 == null) {
163                value2 = "";
164            }
165            elem.setVersion1(value1);
166            elem.setVersion2(value2);
167            if (!value1.equals(value2)) {
168                elem.setStatus(CmsResourceComparison.TYPE_CHANGED);
169            } else {
170                elem.setStatus(CmsResourceComparison.TYPE_UNCHANGED);
171            }
172        }
173        m_elements = new ArrayList<CmsElementComparison>(removed);
174        m_elements.addAll(added);
175        m_elements.addAll(union);
176        Collections.sort(m_elements);
177    }
178
179    /**
180     * Returns the elements.<p>
181     *
182     * @return the elements
183     */
184    public List<CmsElementComparison> getElements() {
185
186        if (m_elements == null) {
187            return Collections.emptyList();
188        }
189        return Collections.unmodifiableList(m_elements);
190    }
191
192    /** Returs a list of all element names of a xml page.<p>
193     *
194     * @param xmlPage the xml page to read the element names from
195     * @return a list of all element names of a xml page
196     */
197    private List<CmsElementComparison> getElements(I_CmsXmlDocument xmlPage) {
198
199        List<CmsElementComparison> elements = new ArrayList<CmsElementComparison>();
200        Iterator<Locale> locales = xmlPage.getLocales().iterator();
201        while (locales.hasNext()) {
202            Locale locale = locales.next();
203            Iterator<String> elementNames = xmlPage.getNames(locale).iterator();
204            while (elementNames.hasNext()) {
205                String elementName = elementNames.next();
206                elements.add(new CmsElementComparison(locale, elementName));
207            }
208        }
209        return elements;
210    }
211}