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