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