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();
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                    int dynamicElementCount = 0;
263    
264                    for (Locale locale : field.getAvailableLocales()) {
265                            List<Serializable> values = field.getValues(locale);
266    
267                            if (dynamicElementCount < values.size()) {
268                                    dynamicElementCount = values.size();
269                            }
270                    }
271    
272                    for (int i = 0; i < dynamicElementCount; i++) {
273                            Element dynamicElementElement = element.addElement(
274                                    "dynamic-element");
275    
276                            dynamicElementElement.addAttribute(
277                                    "default-language-id",
278                                    LocaleUtil.toLanguageId(field.getDefaultLocale()));
279                            dynamicElementElement.addAttribute("name", field.getName());
280    
281                            for (Locale locale : field.getAvailableLocales()) {
282                                    List<Serializable> values = field.getValues(locale);
283    
284                                    if (i >= values.size()) {
285                                            continue;
286                                    }
287    
288                                    Element dynamicContentElement =
289                                            dynamicElementElement.addElement("dynamic-content");
290    
291                                    dynamicContentElement.addAttribute(
292                                            "language-id", LocaleUtil.toLanguageId(locale));
293    
294                                    Serializable value = field.getValue(locale, i);
295    
296                                    updateField(
297                                            dynamicContentElement, locale, field.getName(), value);
298                            }
299                    }
300            }
301    
302            protected void fixElementsDefaultLocale(
303                    Element element, Locale contentDefaultLocale,
304                    Locale contentNewDefaultLocale) {
305    
306                    for (Element dynamicElementElement :
307                                    element.elements(_DYNAMIC_ELEMENT)) {
308    
309                            Element importMetaDataElement =
310                                    (Element)dynamicElementElement.selectSingleNode(
311                                            "meta-data[@locale='" + contentNewDefaultLocale.toString() +
312                                                    "']");
313    
314                            if (importMetaDataElement == null) {
315                                    Element metaDataElement =
316                                            (Element)dynamicElementElement.selectSingleNode(
317                                                    "meta-data[@locale='" +
318                                                            contentDefaultLocale.toString() + "']");
319    
320                                    Element copiedMetadataElement = metaDataElement.createCopy();
321    
322                                    Attribute localeAttribute = copiedMetadataElement.attribute(
323                                            _LOCALE);
324    
325                                    String contentNewDefaultLanguageId = LocaleUtil.toLanguageId(
326                                            contentNewDefaultLocale);
327    
328                                    localeAttribute.setValue(contentNewDefaultLanguageId);
329    
330                                    dynamicElementElement.add(copiedMetadataElement);
331                            }
332    
333                            fixElementsDefaultLocale(
334                                    dynamicElementElement, contentDefaultLocale,
335                                    contentNewDefaultLocale);
336                    }
337            }
338    
339            protected List<Node> getElementsByName(Document document, String name) {
340                    name = HtmlUtil.escapeXPathAttribute(name);
341    
342                    XPath xPathSelector = SAXReaderUtil.createXPath(
343                            "//dynamic-element[@name=".concat(name).concat("]"));
344    
345                    return xPathSelector.selectNodes(document);
346            }
347    
348            protected void updateField(
349                    Element dynamicContentElement, Locale locale, String fieldName,
350                    Serializable fieldValue) {
351    
352                    dynamicContentElement.clearContent();
353    
354                    if (fieldValue instanceof Date) {
355                            Date valueDate = (Date)fieldValue;
356    
357                            fieldValue = valueDate.getTime();
358                    }
359    
360                    String valueString = String.valueOf(fieldValue);
361    
362                    if (valueString != null) {
363                            valueString = valueString.trim();
364                    }
365    
366                    dynamicContentElement.addCDATA(valueString);
367            }
368    
369            private static final String _AVAILABLE_LOCALES = "available-locales";
370    
371            private static final String _DEFAULT_LOCALE = "default-locale";
372    
373            private static final String _DYNAMIC_ELEMENT = "dynamic-element";
374    
375            private static final String _LOCALE = "locale";
376    
377            private static final String _XML_INDENT = "  ";
378    
379    }