001
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.ArrayUtil;
024 import com.liferay.portal.kernel.util.GetterUtil;
025 import com.liferay.portal.kernel.util.HtmlUtil;
026 import com.liferay.portal.kernel.util.ListUtil;
027 import com.liferay.portal.kernel.util.LocaleUtil;
028 import com.liferay.portal.kernel.util.PredicateFilter;
029 import com.liferay.portal.kernel.util.PropsKeys;
030 import com.liferay.portal.kernel.util.PropsUtil;
031 import com.liferay.portal.kernel.util.StringBundler;
032 import com.liferay.portal.kernel.util.StringPool;
033 import com.liferay.portal.kernel.util.StringUtil;
034 import com.liferay.portal.kernel.util.Validator;
035 import com.liferay.portal.kernel.xml.Attribute;
036 import com.liferay.portal.kernel.xml.Document;
037 import com.liferay.portal.kernel.xml.Element;
038 import com.liferay.portal.kernel.xml.Node;
039 import com.liferay.portal.kernel.xml.SAXReaderUtil;
040 import com.liferay.portal.kernel.xml.XPath;
041 import com.liferay.portal.model.CacheField;
042 import com.liferay.portal.model.Group;
043 import com.liferay.portal.service.GroupLocalServiceUtil;
044 import com.liferay.portal.theme.ThemeDisplay;
045 import com.liferay.portal.util.PortalUtil;
046 import com.liferay.portal.util.PropsValues;
047 import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
048 import com.liferay.portlet.dynamicdatamapping.StructureFieldException;
049 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
050 import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
051 import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
052 import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
053 import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
054
055 import java.util.ArrayList;
056 import java.util.HashMap;
057 import java.util.LinkedHashMap;
058 import java.util.List;
059 import java.util.Locale;
060 import java.util.Map;
061 import java.util.Set;
062 import java.util.concurrent.ConcurrentHashMap;
063
064
067 public class DDMStructureImpl extends DDMStructureBaseImpl {
068
069 @Override
070 public String[] getAvailableLanguageIds() {
071 Document document = getDocument();
072
073 Element rootElement = document.getRootElement();
074
075 String availableLocales = rootElement.attributeValue(
076 "available-locales");
077
078 return StringUtil.split(availableLocales);
079 }
080
081 @Override
082 public List<String> getChildrenFieldNames(String fieldName)
083 throws PortalException, SystemException {
084
085 List<String> fieldNames = new ArrayList<String>();
086
087 Map<String, Map<String, String>> fieldsMap = getFieldsMap(true);
088
089 for (Map<String, String> field : fieldsMap.values()) {
090 String parentNameKey = _getPrivateAttributeKey("parentName");
091
092 String parentName = field.get(parentNameKey);
093
094 if (fieldName.equals(parentName)) {
095 fieldNames.add(field.get("name"));
096 }
097 }
098
099 return fieldNames;
100 }
101
102 @Override
103 public String getCompleteXsd() throws PortalException, SystemException {
104 if (getParentStructureId() == 0) {
105 return getXsd();
106 }
107
108 DDMStructure parentStructure =
109 DDMStructureLocalServiceUtil.getStructure(getParentStructureId());
110
111 return _mergeXsds(getXsd(), parentStructure.getCompleteXsd());
112 }
113
114 @Override
115 public String getDefaultLanguageId() {
116 Document document = getDocument();
117
118 if (document == null) {
119 Locale locale = LocaleUtil.getSiteDefault();
120
121 return locale.toString();
122 }
123
124 Element rootElement = document.getRootElement();
125
126 return rootElement.attributeValue("default-locale");
127 }
128
129 @Override
130 public Document getDocument() {
131 if (_document == null) {
132 try {
133 _document = SAXReaderUtil.read(getXsd());
134 }
135 catch (Exception e) {
136 StackTraceElement[] stackTraceElements = e.getStackTrace();
137
138 for (StackTraceElement stackTraceElement : stackTraceElements) {
139 String className = stackTraceElement.getClassName();
140
141 if (className.endsWith("DDMStructurePersistenceTest")) {
142 return null;
143 }
144 }
145
146 _log.error(e, e);
147 }
148 }
149
150 return _document;
151 }
152
153 @Override
154 public String getFieldDataType(String fieldName)
155 throws PortalException, SystemException {
156
157 return getFieldProperty(fieldName, "dataType");
158 }
159
160 @Override
161 public String getFieldLabel(String fieldName, Locale locale)
162 throws PortalException, SystemException {
163
164 return getFieldLabel(fieldName, LocaleUtil.toLanguageId(locale));
165 }
166
167 @Override
168 public String getFieldLabel(String fieldName, String locale)
169 throws PortalException, SystemException {
170
171 return GetterUtil.getString(
172 getFieldProperty(fieldName, "label", locale), fieldName);
173 }
174
175 @Override
176 public Set<String> getFieldNames() throws PortalException, SystemException {
177 Map<String, Map<String, String>> fieldsMap = getFieldsMap();
178
179 return fieldsMap.keySet();
180 }
181
182 @Override
183 public String getFieldProperty(String fieldName, String property)
184 throws PortalException, SystemException {
185
186 return getFieldProperty(fieldName, property, getDefaultLanguageId());
187 }
188
189 @Override
190 public String getFieldProperty(
191 String fieldName, String property, String locale)
192 throws PortalException, SystemException {
193
194 if (!hasField(fieldName)) {
195 throw new StructureFieldException(
196 "Unable to find field " + fieldName);
197 }
198
199 Map<String, Map<String, String>> fieldsMap = getFieldsMap(locale, true);
200
201 Map<String, String> field = fieldsMap.get(fieldName);
202
203 return field.get(property);
204 }
205
206 @Override
207 public boolean getFieldRepeatable(String fieldName)
208 throws PortalException, SystemException {
209
210 return GetterUtil.getBoolean(getFieldProperty(fieldName, "repeatable"));
211 }
212
213 @Override
214 public boolean getFieldRequired(String fieldName)
215 throws PortalException, SystemException {
216
217 return GetterUtil.getBoolean(getFieldProperty(fieldName, "required"));
218 }
219
220 @Override
221 public Map<String, String> getFields(
222 String fieldName, String attributeName, String attributeValue) {
223
224 return getFields(
225 fieldName, attributeName, attributeValue, getDefaultLanguageId());
226 }
227
228 @Override
229 public Map<String, String> getFields(
230 String fieldName, String attributeName, String attributeValue,
231 String locale) {
232
233 try {
234 if ((attributeName == null) || (attributeValue == null)) {
235 return null;
236 }
237
238 Map<String, Map<String, String>> fieldsMap = getTransientFieldsMap(
239 locale);
240
241 for (Map<String, String> fields : fieldsMap.values()) {
242 String parentName = fields.get(
243 _getPrivateAttributeKey("parentName"));
244
245 if (!fieldName.equals(parentName)) {
246 continue;
247 }
248
249 if (attributeValue.equals(fields.get(attributeName))) {
250 return fields;
251 }
252 }
253 }
254 catch (Exception e) {
255 _log.error(e, e);
256 }
257
258 return null;
259 }
260
261 @Override
262 public Map<String, Map<String, String>> getFieldsMap()
263 throws PortalException, SystemException {
264
265 return getFieldsMap(getDefaultLanguageId());
266 }
267
268 @Override
269 public Map<String, Map<String, String>> getFieldsMap(
270 boolean includeTransientFields)
271 throws PortalException, SystemException {
272
273 return getFieldsMap(getDefaultLanguageId(), includeTransientFields);
274 }
275
276 @Override
277 public Map<String, Map<String, String>> getFieldsMap(String locale)
278 throws PortalException, SystemException {
279
280 return getFieldsMap(locale, false);
281 }
282
283 @Override
284 public Map<String, Map<String, String>> getFieldsMap(
285 String locale, boolean includeTransientFields)
286 throws PortalException, SystemException {
287
288 _indexFieldsMap(locale);
289
290 Map<String, Map<String, Map<String, String>>> fieldsMap = null;
291
292 if (includeTransientFields) {
293 fieldsMap = getLocalizedFieldsMap();
294 }
295 else {
296 fieldsMap = getLocalizedPersistentFieldsMap();
297 }
298
299 return fieldsMap.get(locale);
300 }
301
302 @Override
303 public String getFieldTip(String fieldName, Locale locale)
304 throws PortalException, SystemException {
305
306 return getFieldTip(fieldName, LocaleUtil.toLanguageId(locale));
307 }
308
309 @Override
310 public String getFieldTip(String fieldName, String locale)
311 throws PortalException, SystemException {
312
313 return GetterUtil.getString(
314 getFieldProperty(fieldName, "tip", locale), fieldName);
315 }
316
317 @Override
318 public String getFieldType(String fieldName)
319 throws PortalException, SystemException {
320
321 return getFieldProperty(fieldName, "type");
322 }
323
324 @Override
325 public Map<String, Map<String, Map<String, String>>>
326 getLocalizedFieldsMap() {
327
328 if (_localizedFieldsMap == null) {
329 _localizedFieldsMap =
330 new ConcurrentHashMap
331 <String, Map<String, Map<String, String>>>();
332 }
333
334 return _localizedFieldsMap;
335 }
336
337 @Override
338 public Map<String, Map<String, Map<String, String>>>
339 getLocalizedPersistentFieldsMap() {
340
341 if (_localizedPersistentFieldsMap == null) {
342 _localizedPersistentFieldsMap =
343 new ConcurrentHashMap
344 <String, Map<String, Map<String, String>>>();
345 }
346
347 return _localizedPersistentFieldsMap;
348 }
349
350 @Override
351 public Map<String, Map<String, Map<String, String>>>
352 getLocalizedTransientFieldsMap() {
353
354 if (_localizedTransientFieldsMap == null) {
355 _localizedTransientFieldsMap =
356 new ConcurrentHashMap
357 <String, Map<String, Map<String, String>>>();
358 }
359
360 return _localizedTransientFieldsMap;
361 }
362
363 @Override
364 public Map<String, Map<String, String>> getPersistentFieldsMap(
365 String locale)
366 throws PortalException, SystemException {
367
368 _indexFieldsMap(locale);
369
370 Map<String, Map<String, Map<String, String>>>
371 localizedPersistentFieldsMap = getLocalizedPersistentFieldsMap();
372
373 Map<String, Map<String, String>> fieldsMap =
374 localizedPersistentFieldsMap.get(locale);
375
376 return fieldsMap;
377 }
378
379 @Override
380 public List<String> getRootFieldNames()
381 throws PortalException, SystemException {
382
383 List<String> fieldNames = new ArrayList<String>();
384
385 Map<String, Map<String, String>> fieldsMap = getFieldsMap(true);
386
387 for (Map.Entry<String, Map<String, String>> entry :
388 fieldsMap.entrySet()) {
389
390 Map<String, String> field = entry.getValue();
391
392 String parentNameKey = _getPrivateAttributeKey("parentName");
393
394 if (!field.containsKey(parentNameKey)) {
395 fieldNames.add(entry.getKey());
396 }
397 }
398
399 return fieldNames;
400 }
401
402 @Override
403 public List<DDMTemplate> getTemplates() throws SystemException {
404 return DDMTemplateLocalServiceUtil.getTemplates(getStructureId());
405 }
406
407 @Override
408 public Map<String, Map<String, String>> getTransientFieldsMap(String locale)
409 throws PortalException, SystemException {
410
411 _indexFieldsMap(locale);
412
413 Map<String, Map<String, Map<String, String>>>
414 localizedTransientFieldsMap = getLocalizedTransientFieldsMap();
415
416 Map<String, Map<String, String>> fieldsMap =
417 localizedTransientFieldsMap.get(locale);
418
419 return fieldsMap;
420 }
421
422 @Override
423 public String getUnambiguousName(
424 List<DDMStructure> structures, long groupId, final Locale locale)
425 throws PortalException, SystemException {
426
427 if (getGroupId() == groupId ) {
428 return getName(locale);
429 }
430
431 boolean hasAmbiguousName = ListUtil.exists(
432 structures,
433 new PredicateFilter<DDMStructure>() {
434
435 @Override
436 public boolean filter(DDMStructure structure) {
437 String name = structure.getName(locale);
438
439 if (name.equals(getName(locale)) &&
440 (structure.getStructureId() != getStructureId())) {
441
442 return true;
443 }
444
445 return false;
446 }
447
448 });
449
450 if (hasAmbiguousName) {
451 Group group = GroupLocalServiceUtil.getGroup(getGroupId());
452
453 return group.getUnambiguousName(getName(locale), locale);
454 }
455
456 return getName(locale);
457 }
458
459
468 @Override
469 public String getWebDavURL(ThemeDisplay themeDisplay, String webDAVToken) {
470 StringBundler sb = new StringBundler(11);
471
472 boolean secure = false;
473
474 if (themeDisplay.isSecure() ||
475 PropsValues.WEBDAV_SERVLET_HTTPS_REQUIRED) {
476
477 secure = true;
478 }
479
480 String portalURL = PortalUtil.getPortalURL(
481 themeDisplay.getServerName(), themeDisplay.getServerPort(), secure);
482
483 sb.append(portalURL);
484
485 sb.append(themeDisplay.getPathContext());
486 sb.append(StringPool.SLASH);
487 sb.append("webdav");
488
489 Group group = themeDisplay.getScopeGroup();
490
491 sb.append(group.getFriendlyURL());
492
493 sb.append(StringPool.SLASH);
494 sb.append(webDAVToken);
495 sb.append(StringPool.SLASH);
496 sb.append("Structures");
497 sb.append(StringPool.SLASH);
498 sb.append(getStructureId());
499
500 return sb.toString();
501 }
502
503 @Override
504 public boolean hasField(String fieldName)
505 throws PortalException, SystemException {
506
507 Map<String, Map<String, String>> fieldsMap = getFieldsMap(true);
508
509 if (fieldsMap.containsKey(fieldName)) {
510 return true;
511 }
512
513 try {
514 DDMStructure parentStructure =
515 DDMStructureLocalServiceUtil.getStructure(
516 getParentStructureId());
517
518 return parentStructure.hasField(fieldName);
519 }
520 catch (NoSuchStructureException nsse) {
521 return false;
522 }
523 }
524
525 @Override
526 public boolean isFieldPrivate(String fieldName)
527 throws PortalException, SystemException {
528
529 return GetterUtil.getBoolean(getFieldProperty(fieldName, "private"));
530 }
531
532 @Override
533 public boolean isFieldRepeatable(String fieldName)
534 throws PortalException, SystemException {
535
536 return GetterUtil.getBoolean(getFieldProperty(fieldName, "repeatable"));
537 }
538
539 @Override
540 public boolean isFieldTransient(String fieldName)
541 throws PortalException, SystemException {
542
543 if (!hasField(fieldName)) {
544 throw new StructureFieldException();
545 }
546
547 Map<String, Map<String, String>> transientFieldsMap =
548 getTransientFieldsMap(getDefaultLanguageId());
549
550 return transientFieldsMap.containsKey(fieldName);
551 }
552
553 @Override
554 public void prepareLocalizedFieldsForImport(Locale defaultImportLocale)
555 throws LocaleException {
556
557 super.prepareLocalizedFieldsForImport(defaultImportLocale);
558
559 Locale ddmStructureDefaultLocale = LocaleUtil.fromLanguageId(
560 getDefaultLanguageId());
561
562 try {
563 setXsd(
564 DDMXMLUtil.updateXMLDefaultLocale(
565 getXsd(), ddmStructureDefaultLocale, defaultImportLocale));
566 }
567 catch (Exception e) {
568 throw new LocaleException(LocaleException.TYPE_EXPORT_IMPORT, e);
569 }
570 }
571
572 @Override
573 public void setDocument(Document document) {
574 _document = document;
575 }
576
577 @Override
578 public void setLocalizedFieldsMap(
579 Map<String, Map<String, Map<String, String>>> localizedFieldsMap) {
580
581 _localizedFieldsMap = localizedFieldsMap;
582 }
583
584 @Override
585 public void setLocalizedPersistentFieldsMap(
586 Map<String, Map<String, Map<String, String>>>
587 localizedPersistentFieldsMap) {
588
589 _localizedPersistentFieldsMap = localizedPersistentFieldsMap;
590 }
591
592 @Override
593 public void setLocalizedTransientFieldsMap(
594 Map<String, Map<String, Map<String, String>>>
595 localizedTransientFieldsMap) {
596
597 _localizedTransientFieldsMap = localizedTransientFieldsMap;
598 }
599
600 @Override
601 public void setXsd(String xsd) {
602 super.setXsd(xsd);
603
604 _document = null;
605 _localizedFieldsMap = null;
606 _localizedPersistentFieldsMap = null;
607 _localizedTransientFieldsMap = null;
608 }
609
610 private Map<String, String> _getField(Element element, String locale) {
611 Map<String, String> field = new HashMap<String, String>();
612
613 String[] availableLanguageIds = getAvailableLanguageIds();
614
615 if ((locale != null) &&
616 !ArrayUtil.contains(availableLanguageIds, locale)) {
617
618 locale = getDefaultLanguageId();
619 }
620
621 locale = HtmlUtil.escapeXPathAttribute(locale);
622
623 String xPathExpression =
624 "meta-data[@locale=".concat(locale).concat("]");
625
626 XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
627
628 Node node = xPathSelector.selectSingleNode(element);
629
630 Element metaDataElement = (Element)node.asXPathResult(node.getParent());
631
632 if (metaDataElement != null) {
633 List<Element> childMetaDataElements = metaDataElement.elements();
634
635 for (Element childMetaDataElement : childMetaDataElements) {
636 String name = childMetaDataElement.attributeValue("name");
637 String value = childMetaDataElement.getText();
638
639 field.put(name, value);
640 }
641 }
642
643 for (Attribute attribute : element.attributes()) {
644 field.put(attribute.getName(), attribute.getValue());
645 }
646
647 Element parentElement = element.getParent();
648
649 if (parentElement != null) {
650 String parentName = parentElement.attributeValue("name");
651
652 if (Validator.isNotNull(parentName)) {
653 field.put(_getPrivateAttributeKey("parentName"), parentName);
654 }
655 }
656
657 return field;
658 }
659
660 private String _getPrivateAttributeKey(String attributeName) {
661 return StringPool.UNDERLINE.concat(attributeName).concat(
662 StringPool.UNDERLINE);
663 }
664
665 private Map<String, String> _getPrivateField(String privateFieldName) {
666 Map<String, String> privateField = new HashMap<String, String>();
667
668 String dataType = PropsUtil.get(
669 PropsKeys.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_DATATYPE,
670 new Filter(privateFieldName));
671
672 privateField.put("dataType", dataType);
673
674 privateField.put("name", privateFieldName);
675 privateField.put("private", Boolean.TRUE.toString());
676
677 String repeatable = PropsUtil.get(
678 PropsKeys.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_REPEATABLE,
679 new Filter(privateFieldName));
680
681 privateField.put("repeatable", repeatable);
682
683 return privateField;
684 }
685
686 private void _indexFieldsMap(String locale)
687 throws PortalException, SystemException {
688
689 Map<String, Map<String, Map<String, String>>> localizedFieldsMap =
690 getLocalizedFieldsMap();
691
692 Map<String, Map<String, String>> fieldsMap = localizedFieldsMap.get(
693 locale);
694
695 Map<String, Map<String, Map<String, String>>>
696 localizedPersistentFieldsMap = getLocalizedPersistentFieldsMap();
697
698 Map<String, Map<String, String>> persistentFieldsMap =
699 localizedPersistentFieldsMap.get(locale);
700
701 Map<String, Map<String, Map<String, String>>>
702 localizedTransientFieldsMap = getLocalizedTransientFieldsMap();
703
704 Map<String, Map<String, String>> transientFieldsMap =
705 localizedTransientFieldsMap.get(locale);
706
707 if (fieldsMap != null) {
708 return;
709 }
710
711 fieldsMap = new LinkedHashMap<String, Map<String, String>>();
712 persistentFieldsMap = new LinkedHashMap<String, Map<String, String>>();
713 transientFieldsMap = new LinkedHashMap<String, Map<String, String>>();
714
715 if (getParentStructureId() > 0) {
716 DDMStructure parentStructure =
717 DDMStructureLocalServiceUtil.getStructure(
718 getParentStructureId());
719
720 fieldsMap.putAll(parentStructure.getFieldsMap(locale, true));
721 persistentFieldsMap.putAll(
722 parentStructure.getPersistentFieldsMap(locale));
723 transientFieldsMap.putAll(
724 parentStructure.getTransientFieldsMap(locale));
725 }
726
727 XPath xPathSelector = SAXReaderUtil.createXPath("
728
729 List<Node> nodes = xPathSelector.selectNodes(getDocument());
730
731 for (Node node : nodes) {
732 Element element = (Element)node;
733
734 String name = element.attributeValue("name");
735
736 fieldsMap.put(name, _getField(element, locale));
737
738 if (Validator.isNotNull(element.attributeValue("dataType"))) {
739 persistentFieldsMap.put(name, _getField(element, locale));
740 }
741 else {
742 transientFieldsMap.put(name, _getField(element, locale));
743 }
744 }
745
746 String[] privateFieldNames =
747 PropsValues.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_NAMES;
748
749 for (String privateFieldName : privateFieldNames) {
750 Map<String, String> privateField = _getPrivateField(
751 privateFieldName);
752
753 fieldsMap.put(privateFieldName, privateField);
754 persistentFieldsMap.put(privateFieldName, privateField);
755 }
756
757 localizedFieldsMap.put(locale, fieldsMap);
758 localizedPersistentFieldsMap.put(locale, persistentFieldsMap);
759 localizedTransientFieldsMap.put(locale, transientFieldsMap);
760 }
761
762 private String _mergeXsds(String xsd1, String xsd2) throws SystemException {
763 try {
764 Document document1 = SAXReaderUtil.read(xsd1);
765 Document document2 = SAXReaderUtil.read(xsd2);
766
767 Element rootElement1 = document1.getRootElement();
768 Element rootElement2 = document2.getRootElement();
769
770 for (Element element : rootElement1.elements()) {
771 rootElement1.remove(element);
772
773 rootElement2.add(element);
774 }
775
776 return rootElement2.formattedString();
777 }
778 catch (Exception e) {
779 throw new SystemException(e);
780 }
781 }
782
783 private static Log _log = LogFactoryUtil.getLog(DDMStructureImpl.class);
784
785 @CacheField
786 private Document _document;
787
788 @CacheField
789 private Map<String, Map<String, Map<String, String>>> _localizedFieldsMap;
790
791 @CacheField
792 private Map<String, Map<String, Map<String, String>>>
793 _localizedPersistentFieldsMap;
794
795 @CacheField
796 private Map<String, Map<String, Map<String, String>>>
797 _localizedTransientFieldsMap;
798
799 }