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.portlet.dynamicdatamapping.util;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
020    import com.liferay.portal.kernel.util.HtmlUtil;
021    import com.liferay.portal.kernel.util.LocaleUtil;
022    import com.liferay.portal.kernel.util.StringBundler;
023    import com.liferay.portal.kernel.util.StringPool;
024    import com.liferay.portal.kernel.util.StringUtil;
025    import com.liferay.portal.kernel.xml.Attribute;
026    import com.liferay.portal.kernel.xml.Document;
027    import com.liferay.portal.kernel.xml.DocumentException;
028    import com.liferay.portal.kernel.xml.Element;
029    import com.liferay.portal.kernel.xml.Node;
030    import com.liferay.portal.kernel.xml.SAXReaderUtil;
031    import com.liferay.portal.kernel.xml.XPath;
032    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
033    import com.liferay.portlet.dynamicdatamapping.storage.Field;
034    import com.liferay.portlet.dynamicdatamapping.storage.FieldConstants;
035    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
036    import com.liferay.util.xml.XMLFormatter;
037    
038    import java.io.IOException;
039    import java.io.Serializable;
040    
041    import java.util.Date;
042    import java.util.Iterator;
043    import java.util.List;
044    import java.util.Locale;
045    
046    /**
047     * @author Bruno Basto
048     * @author Brian Wing Shun Chan
049     */
050    @DoPrivileged
051    public class DDMXMLImpl implements DDMXML {
052    
053            public String formatXML(Document document) throws SystemException {
054                    try {
055                            return document.formattedString(_XML_INDENT);
056                    }
057                    catch (IOException ioe) {
058                            throw new SystemException(ioe);
059                    }
060            }
061    
062            public String formatXML(String xml) throws SystemException {
063    
064                    // This is only supposed to format your xml, however, it will also
065                    // unwantingly change © and other characters like it into their
066                    // respective readable versions
067    
068                    try {
069                            xml = StringUtil.replace(xml, "&#", "[$SPECIAL_CHARACTER$]");
070                            xml = XMLFormatter.toString(xml, _XML_INDENT);
071                            xml = StringUtil.replace(xml, "[$SPECIAL_CHARACTER$]", "&#");
072    
073                            return xml;
074                    }
075                    catch (IOException ioe) {
076                            throw new SystemException(ioe);
077                    }
078                    catch (org.dom4j.DocumentException de) {
079                            throw new SystemException(de);
080                    }
081            }
082    
083            public Fields getFields(DDMStructure structure, String xml)
084                    throws PortalException, SystemException {
085    
086                    return getFields(structure, null, xml, null);
087            }
088    
089            public Fields getFields(
090                            DDMStructure structure, XPath xPath, String xml,
091                            List<String> fieldNames)
092                    throws PortalException, SystemException {
093    
094                    Document document = null;
095    
096                    try {
097                            document = SAXReaderUtil.read(xml);
098                    }
099                    catch (DocumentException e) {
100                            return null;
101                    }
102    
103                    if ((xPath != null) && !xPath.booleanValueOf(document)) {
104                            return null;
105                    }
106    
107                    Fields fields = new Fields();
108    
109                    Element rootElement = document.getRootElement();
110    
111                    List<Element> dynamicElementElements = rootElement.elements(
112                            "dynamic-element");
113    
114                    for (Element dynamicElementElement : dynamicElementElements) {
115                            String fieldName = dynamicElementElement.attributeValue("name");
116    
117                            if (!structure.hasField(fieldName) ||
118                                    ((fieldNames != null) && !fieldNames.contains(fieldName))) {
119    
120                                    continue;
121                            }
122    
123                            String fieldDataType = structure.getFieldDataType(fieldName);
124    
125                            List<Element> dynamicContentElements =
126                                    dynamicElementElement.elements("dynamic-content");
127    
128                            for (Element dynamicContentElement : dynamicContentElements) {
129                                    String fieldValue = dynamicContentElement.getText();
130    
131                                    String languageId = dynamicContentElement.attributeValue(
132                                            "language-id");
133    
134                                    Locale locale = LocaleUtil.fromLanguageId(languageId);
135    
136                                    Serializable fieldValueSerializable =
137                                            FieldConstants.getSerializable(fieldDataType, fieldValue);
138    
139                                    Field field = fields.get(fieldName);
140    
141                                    if (field == null) {
142                                            field = new Field();
143    
144                                            String defaultLanguageId =
145                                                    dynamicElementElement.attributeValue(
146                                                            "default-language-id");
147    
148                                            Locale defaultLocale = LocaleUtil.fromLanguageId(
149                                                    defaultLanguageId);
150    
151                                            field.setDefaultLocale(defaultLocale);
152    
153                                            field.setDDMStructureId(structure.getStructureId());
154                                            field.setName(fieldName);
155                                            field.setValue(locale, fieldValueSerializable);
156    
157                                            fields.put(field);
158                                    }
159                                    else {
160                                            field.addValue(locale, fieldValueSerializable);
161                                    }
162                            }
163                    }
164    
165                    return fields;
166            }
167    
168            public String getXML(Document document, Fields fields)
169                    throws SystemException {
170    
171                    Element rootElement = null;
172    
173                    try {
174                            if (document != null) {
175                                    rootElement = document.getRootElement();
176                            }
177                            else {
178                                    document = SAXReaderUtil.createDocument();
179    
180                                    rootElement = document.addElement("root");
181                            }
182    
183                            Iterator<Field> itr = fields.iterator(true);
184    
185                            while (itr.hasNext()) {
186                                    Field field = itr.next();
187    
188                                    List<Node> nodes = getElementsByName(document, field.getName());
189    
190                                    for (Node node : nodes) {
191                                            document.remove(node);
192                                    }
193    
194                                    appendField(rootElement, field);
195                            }
196    
197                            return document.formattedString();
198                    }
199                    catch (IOException ioe) {
200                            throw new SystemException(ioe);
201                    }
202            }
203    
204            public String getXML(Fields fields) throws SystemException {
205                    return getXML(null, fields);
206            }
207    
208            public String updateXMLDefaultLocale(
209                            String xml, Locale contentDefaultLocale,
210                            Locale contentNewDefaultLocale)
211                    throws SystemException {
212    
213                    try {
214                            if (LocaleUtil.equals(
215                                            contentDefaultLocale, contentNewDefaultLocale)) {
216    
217                                    return xml;
218                            }
219    
220                            Document document = SAXReaderUtil.read(xml);
221    
222                            Element rootElement = document.getRootElement();
223    
224                            Attribute availableLocalesAttribute = rootElement.attribute(
225                                    _AVAILABLE_LOCALES);
226    
227                            String contentNewDefaultLanguageId = LocaleUtil.toLanguageId(
228                                    contentNewDefaultLocale);
229    
230                            String availableLocalesAttributeValue =
231                                    availableLocalesAttribute.getValue();
232    
233                            if (!availableLocalesAttributeValue.contains(
234                                            contentNewDefaultLanguageId)) {
235    
236                                    StringBundler sb = new StringBundler(3);
237    
238                                    sb.append(availableLocalesAttribute.getValue());
239                                    sb.append(StringPool.COMMA);
240                                    sb.append(contentNewDefaultLanguageId);
241    
242                                    availableLocalesAttribute.setValue(sb.toString());
243                            }
244    
245                            Attribute defaultLocaleAttribute = rootElement.attribute(
246                                    _DEFAULT_LOCALE);
247    
248                            defaultLocaleAttribute.setValue(contentNewDefaultLanguageId);
249    
250                            fixElementsDefaultLocale(
251                                    rootElement, contentDefaultLocale, contentNewDefaultLocale);
252    
253                            return document.formattedString();
254                    }
255                    catch (DocumentException de) {
256                            throw new SystemException(de);
257                    }
258                    catch (IOException ioe) {
259                            throw new SystemException(ioe);
260                    }
261            }
262    
263            protected void appendField(Element element, Field field) {
264                    Element dynamicElementElement = element.addElement("dynamic-element");
265    
266                    dynamicElementElement.addAttribute(
267                            "default-language-id",
268                            LocaleUtil.toLanguageId(field.getDefaultLocale()));
269                    dynamicElementElement.addAttribute("name", field.getName());
270    
271                    for (Locale locale : field.getAvailableLocales()) {
272                            List<Serializable> values = field.getValues(locale);
273    
274                            for (Serializable value : values) {
275                                    Element dynamicContentElement =
276                                            dynamicElementElement.addElement("dynamic-content");
277    
278                                    dynamicContentElement.addAttribute(
279                                            "language-id", LocaleUtil.toLanguageId(locale));
280    
281                                    updateField(dynamicContentElement, value);
282                            }
283                    }
284            }
285    
286            protected void fixElementsDefaultLocale(
287                    Element element, Locale contentDefaultLocale,
288                    Locale contentNewDefaultLocale) {
289    
290                    for (Element dynamicElementElement :
291                                    element.elements(_DYNAMIC_ELEMENT)) {
292    
293                            Element importMetaDataElement =
294                                    (Element)dynamicElementElement.selectSingleNode(
295                                            "meta-data[@locale='" + contentNewDefaultLocale.toString() +
296                                                    "']");
297    
298                            if (importMetaDataElement == null) {
299                                    Element metaDataElement =
300                                            (Element)dynamicElementElement.selectSingleNode(
301                                                    "meta-data[@locale='" +
302                                                            contentDefaultLocale.toString() + "']");
303    
304                                    Element copiedMetadataElement = metaDataElement.createCopy();
305    
306                                    Attribute localeAttribute = copiedMetadataElement.attribute(
307                                            _LOCALE);
308    
309                                    String contentNewDefaultLanguageId = LocaleUtil.toLanguageId(
310                                            contentNewDefaultLocale);
311    
312                                    localeAttribute.setValue(contentNewDefaultLanguageId);
313    
314                                    dynamicElementElement.add(copiedMetadataElement);
315                            }
316    
317                            fixElementsDefaultLocale(
318                                    dynamicElementElement, contentDefaultLocale,
319                                    contentNewDefaultLocale);
320                    }
321            }
322    
323            protected List<Node> getElementsByName(Document document, String name) {
324                    name = HtmlUtil.escapeXPathAttribute(name);
325    
326                    XPath xPathSelector = SAXReaderUtil.createXPath(
327                            "//dynamic-element[@name=".concat(name).concat("]"));
328    
329                    return xPathSelector.selectNodes(document);
330            }
331    
332            protected void updateField(
333                    Element dynamicContentElement, Serializable fieldValue) {
334    
335                    dynamicContentElement.clearContent();
336    
337                    if (fieldValue instanceof Date) {
338                            Date valueDate = (Date)fieldValue;
339    
340                            fieldValue = valueDate.getTime();
341                    }
342    
343                    String valueString = String.valueOf(fieldValue);
344    
345                    dynamicContentElement.addCDATA(valueString.trim());
346            }
347    
348            private static final String _AVAILABLE_LOCALES = "available-locales";
349    
350            private static final String _DEFAULT_LOCALE = "default-locale";
351    
352            private static final String _DYNAMIC_ELEMENT = "dynamic-element";
353    
354            private static final String _LOCALE = "locale";
355    
356            private static final String _XML_INDENT = "  ";
357    
358    }