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.model.impl;
016    
017    import com.liferay.portal.LocaleException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.HtmlUtil;
023    import com.liferay.portal.kernel.util.ListUtil;
024    import com.liferay.portal.kernel.util.LocaleUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.xml.Attribute;
029    import com.liferay.portal.kernel.xml.Document;
030    import com.liferay.portal.kernel.xml.Element;
031    import com.liferay.portal.kernel.xml.Node;
032    import com.liferay.portal.kernel.xml.SAXReaderUtil;
033    import com.liferay.portal.kernel.xml.XPath;
034    import com.liferay.portal.model.CacheField;
035    import com.liferay.portlet.dynamicdatamapping.StructureFieldException;
036    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
037    import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
038    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
039    
040    import java.util.HashMap;
041    import java.util.Iterator;
042    import java.util.LinkedHashMap;
043    import java.util.List;
044    import java.util.Locale;
045    import java.util.Map;
046    import java.util.Set;
047    import java.util.concurrent.ConcurrentHashMap;
048    
049    /**
050     * @author Brian Wing Shun Chan
051     */
052    public class DDMStructureImpl extends DDMStructureBaseImpl {
053    
054            public DDMStructureImpl() {
055            }
056    
057            @Override
058            public List<String> getAvailableLocales() {
059                    Document document = getDocument();
060    
061                    Element rootElement = document.getRootElement();
062    
063                    String availableLocales = rootElement.attributeValue(
064                            "available-locales");
065    
066                    return ListUtil.fromArray(StringUtil.split(availableLocales));
067            }
068    
069            @Override
070            public String getDefaultLocale() {
071                    Document document = getDocument();
072    
073                    if (document == null) {
074                            Locale locale = LocaleUtil.getDefault();
075    
076                            return locale.toString();
077                    }
078    
079                    Element rootElement = document.getRootElement();
080    
081                    return rootElement.attributeValue("default-locale");
082            }
083    
084            @Override
085            public Document getDocument() {
086                    if (_document == null) {
087                            try {
088                                    _document = SAXReaderUtil.read(getXsd());
089                            }
090                            catch (Exception e) {
091                                    StackTraceElement[] stackTraceElements = e.getStackTrace();
092    
093                                    for (StackTraceElement stackTraceElement : stackTraceElements) {
094                                            String className = stackTraceElement.getClassName();
095    
096                                            if (className.endsWith("DDMStructurePersistenceTest")) {
097                                                    return null;
098                                            }
099                                    }
100    
101                                    _log.error(e, e);
102                            }
103                    }
104    
105                    return _document;
106            }
107    
108            @Override
109            public String getFieldDataType(String fieldName)
110                    throws StructureFieldException {
111    
112                    return getFieldProperty(fieldName, "dataType");
113            }
114    
115            @Override
116            public String getFieldLabel(String fieldName, Locale locale)
117                    throws StructureFieldException {
118    
119                    return getFieldLabel(fieldName, LocaleUtil.toLanguageId(locale));
120            }
121    
122            @Override
123            public String getFieldLabel(String fieldName, String locale)
124                    throws StructureFieldException {
125    
126                    return GetterUtil.getString(
127                            getFieldProperty(fieldName, "label", locale), fieldName);
128            }
129    
130            @Override
131            public Set<String> getFieldNames() {
132                    Map<String, Map<String, String>> fieldsMap = getFieldsMap();
133    
134                    return fieldsMap.keySet();
135            }
136    
137            @Override
138            public String getFieldProperty(String fieldName, String property)
139                    throws StructureFieldException {
140    
141                    return getFieldProperty(fieldName, property, getDefaultLocale());
142            }
143    
144            @Override
145            public String getFieldProperty(
146                            String fieldName, String property, String locale)
147                    throws StructureFieldException {
148    
149                    if (!hasField(fieldName)) {
150                            throw new StructureFieldException();
151                    }
152    
153                    Map<String, Map<String, String>> fieldsMap = getFieldsMap(locale);
154    
155                    Map<String, String> field = fieldsMap.get(fieldName);
156    
157                    return field.get(property);
158            }
159    
160            @Override
161            public boolean getFieldRequired(String fieldName)
162                    throws StructureFieldException {
163    
164                    return GetterUtil.getBoolean(getFieldProperty(fieldName, "required"));
165            }
166    
167            @Override
168            public Map<String, String> getFields(
169                    String fieldName, String attributeName, String attributeValue) {
170    
171                    return getFields(
172                            fieldName, attributeName, attributeValue, getDefaultLocale());
173            }
174    
175            @Override
176            public Map<String, String> getFields(
177                    String fieldName, String attributeName, String attributeValue,
178                    String locale) {
179    
180                    try {
181                            if ((attributeName == null) || (attributeValue == null)) {
182                                    return null;
183                            }
184    
185                            Map<String, Map<String, String>> fieldsMap = getTransientFieldsMap(
186                                    locale);
187    
188                            for (Map<String, String> fields : fieldsMap.values()) {
189                                    String parentName = fields.get(
190                                            _getPrivateAttributeKey("parentName"));
191    
192                                    if (!fieldName.equals(parentName)) {
193                                            continue;
194                                    }
195    
196                                    if (attributeValue.equals(fields.get(attributeName))) {
197                                            return fields;
198                                    }
199                            }
200                    }
201                    catch (Exception e) {
202                            _log.error(e, e);
203                    }
204    
205                    return null;
206            }
207    
208            @Override
209            public Map<String, Map<String, String>> getFieldsMap() {
210                    return getFieldsMap(getDefaultLocale());
211            }
212    
213            @Override
214            public Map<String, Map<String, String>> getFieldsMap(String locale) {
215    
216                    _indexFieldsMap(locale);
217    
218                    Map<String, Map<String, String>> fieldsMap = _localizedFieldsMap.get(
219                            locale);
220    
221                    return fieldsMap;
222            }
223    
224            @Override
225            public String getFieldType(String fieldName)
226                    throws StructureFieldException {
227    
228                    return getFieldProperty(fieldName, "type");
229            }
230    
231            @Override
232            public Map<String, Map<String, Map<String, String>>>
233                    getLocalizedFieldsMap() {
234    
235                    return _localizedFieldsMap;
236            }
237    
238            @Override
239            public Map<String, Map<String, Map<String, String>>>
240                    getLocalizedTransientFieldsMap() {
241    
242                    return _localizedTransientFieldsMap;
243            }
244    
245            @Override
246            public List<DDMTemplate> getTemplates() throws SystemException {
247                    return DDMTemplateLocalServiceUtil.getTemplates(getStructureId());
248            }
249    
250            @Override
251            public Map<String, Map<String, String>> getTransientFieldsMap(
252                    String locale) {
253    
254                    _indexFieldsMap(locale);
255    
256                    Map<String, Map<String, String>> fieldsMap =
257                            _localizedTransientFieldsMap.get(locale);
258    
259                    return fieldsMap;
260            }
261    
262            @Override
263            public boolean hasField(String fieldName) {
264                    Map<String, Map<String, String>> fieldsMap = getFieldsMap();
265    
266                    return fieldsMap.containsKey(fieldName);
267            }
268    
269            @Override
270            public void prepareLocalizedFieldsForImport(Locale defaultImportLocale)
271                    throws LocaleException {
272    
273                    super.prepareLocalizedFieldsForImport(defaultImportLocale);
274    
275                    Locale ddmStructureDefaultLocale = LocaleUtil.fromLanguageId(
276                            getDefaultLocale());
277    
278                    try {
279                            setXsd(
280                                    DDMXMLUtil.updateXMLDefaultLocale(
281                                            getXsd(), ddmStructureDefaultLocale, defaultImportLocale));
282                    }
283                    catch (Exception e) {
284                            throw new LocaleException(e);
285                    }
286            }
287    
288            @Override
289            public void setDocument(Document document) {
290                    _document = document;
291            }
292    
293            @Override
294            public void setLocalizedFieldsMap(
295                    Map<String, Map<String, Map<String, String>>> localizedFieldsMap) {
296    
297                    _localizedFieldsMap = localizedFieldsMap;
298            }
299    
300            @Override
301            public void setLocalizedTransientFieldsMap(
302                    Map<String, Map<String, Map<String, String>>>
303                            localizedTransientFieldsMap) {
304    
305                    _localizedTransientFieldsMap = localizedTransientFieldsMap;
306            }
307    
308            @Override
309            public void setXsd(String xsd) {
310                    super.setXsd(xsd);
311    
312                    _document = null;
313                    _localizedFieldsMap.clear();
314                    _localizedTransientFieldsMap.clear();
315            }
316    
317            private Map<String, String> _getField(Element element, String locale) {
318                    Map<String, String> field = new HashMap<String, String>();
319    
320                    List<String> availableLocales = getAvailableLocales();
321    
322                    if ((locale != null) && !availableLocales.contains(locale)) {
323                            locale = getDefaultLocale();
324                    }
325    
326                    locale = HtmlUtil.escapeXPathAttribute(locale);
327    
328                    String xPathExpression =
329                            "meta-data[@locale=".concat(locale).concat("]");
330    
331                    XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
332    
333                    Node node = xPathSelector.selectSingleNode(element);
334    
335                    Element metaDataElement = (Element)node.asXPathResult(node.getParent());
336    
337                    if (metaDataElement != null) {
338                            List<Element> childMetaDataElements = metaDataElement.elements();
339    
340                            for (Element childMetaDataElement : childMetaDataElements) {
341                                    String name = childMetaDataElement.attributeValue("name");
342                                    String value = childMetaDataElement.getText();
343    
344                                    field.put(name, value);
345                            }
346                    }
347    
348                    for (Attribute attribute : element.attributes()) {
349                            field.put(attribute.getName(), attribute.getValue());
350                    }
351    
352                    Element parentElement = element.getParent();
353    
354                    if (parentElement != null) {
355                            String parentName = parentElement.attributeValue("name");
356    
357                            if (Validator.isNotNull(parentName)) {
358                                    field.put(_getPrivateAttributeKey("parentName"), parentName);
359                            }
360                    }
361    
362                    return field;
363            }
364    
365            private String _getPrivateAttributeKey(String attributeName) {
366                    return StringPool.UNDERLINE.concat(attributeName).concat(
367                            StringPool.UNDERLINE);
368            }
369    
370            private void _indexFieldsMap(String locale) {
371                    Map<String, Map<String, String>> fieldsMap = _localizedFieldsMap.get(
372                            locale);
373                    Map<String, Map<String, String>> transientFieldsMap =
374                            _localizedTransientFieldsMap.get(locale);
375    
376                    if (fieldsMap != null) {
377                            return;
378                    }
379    
380                    fieldsMap = new LinkedHashMap<String, Map<String, String>>();
381                    transientFieldsMap = new LinkedHashMap<String, Map<String, String>>();
382    
383                    XPath xPathSelector = SAXReaderUtil.createXPath("//dynamic-element");
384    
385                    List<Node> nodes = xPathSelector.selectNodes(getDocument());
386    
387                    Iterator<Node> itr = nodes.iterator();
388    
389                    while (itr.hasNext()) {
390                            Element element = (Element)itr.next();
391    
392                            String name = element.attributeValue("name");
393    
394                            if (Validator.isNotNull(element.attributeValue("dataType"))) {
395                                    fieldsMap.put(name, _getField(element, locale));
396                            }
397                            else {
398                                    transientFieldsMap.put(name, _getField(element, locale));
399                            }
400                    }
401    
402                    _localizedFieldsMap.put(locale, fieldsMap);
403                    _localizedTransientFieldsMap.put(locale, transientFieldsMap);
404            }
405    
406            private static Log _log = LogFactoryUtil.getLog(DDMStructureImpl.class);
407    
408            @CacheField
409            private Document _document;
410    
411            @CacheField
412            private Map<String, Map<String, Map<String, String>>> _localizedFieldsMap =
413                    new ConcurrentHashMap<String, Map<String, Map<String, String>>>();
414    
415            @CacheField
416            private Map<String, Map<String, Map<String, String>>>
417                    _localizedTransientFieldsMap =
418                            new ConcurrentHashMap<String, Map<String, Map<String, String>>>();
419    
420    }