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