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