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