001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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                    }
194    
195                    Map<String, Map<String, String>> fieldsMap = getFieldsMap(locale, true);
196    
197                    Map<String, String> field = fieldsMap.get(fieldName);
198    
199                    return field.get(property);
200            }
201    
202            @Override
203            public boolean getFieldRepeatable(String fieldName)
204                    throws PortalException, SystemException {
205    
206                    return GetterUtil.getBoolean(getFieldProperty(fieldName, "repeatable"));
207            }
208    
209            @Override
210            public boolean getFieldRequired(String fieldName)
211                    throws PortalException, SystemException {
212    
213                    return GetterUtil.getBoolean(getFieldProperty(fieldName, "required"));
214            }
215    
216            @Override
217            public Map<String, String> getFields(
218                    String fieldName, String attributeName, String attributeValue) {
219    
220                    return getFields(
221                            fieldName, attributeName, attributeValue, getDefaultLanguageId());
222            }
223    
224            @Override
225            public Map<String, String> getFields(
226                    String fieldName, String attributeName, String attributeValue,
227                    String locale) {
228    
229                    try {
230                            if ((attributeName == null) || (attributeValue == null)) {
231                                    return null;
232                            }
233    
234                            Map<String, Map<String, String>> fieldsMap = getTransientFieldsMap(
235                                    locale);
236    
237                            for (Map<String, String> fields : fieldsMap.values()) {
238                                    String parentName = fields.get(
239                                            _getPrivateAttributeKey("parentName"));
240    
241                                    if (!fieldName.equals(parentName)) {
242                                            continue;
243                                    }
244    
245                                    if (attributeValue.equals(fields.get(attributeName))) {
246                                            return fields;
247                                    }
248                            }
249                    }
250                    catch (Exception e) {
251                            _log.error(e, e);
252                    }
253    
254                    return null;
255            }
256    
257            @Override
258            public Map<String, Map<String, String>> getFieldsMap()
259                    throws PortalException, SystemException {
260    
261                    return getFieldsMap(getDefaultLanguageId());
262            }
263    
264            @Override
265            public Map<String, Map<String, String>> getFieldsMap(
266                            boolean includeTransientFields)
267                    throws PortalException, SystemException {
268    
269                    return getFieldsMap(getDefaultLanguageId(), includeTransientFields);
270            }
271    
272            @Override
273            public Map<String, Map<String, String>> getFieldsMap(String locale)
274                    throws PortalException, SystemException {
275    
276                    return getFieldsMap(locale, false);
277            }
278    
279            @Override
280            public Map<String, Map<String, String>> getFieldsMap(
281                            String locale, boolean includeTransientFields)
282                    throws PortalException, SystemException {
283    
284                    _indexFieldsMap(locale);
285    
286                    Map<String, Map<String, Map<String, String>>> fieldsMap = null;
287    
288                    if (includeTransientFields) {
289                            fieldsMap = getLocalizedFieldsMap();
290                    }
291                    else {
292                            fieldsMap = getLocalizedPersistentFieldsMap();
293                    }
294    
295                    return fieldsMap.get(locale);
296            }
297    
298            @Override
299            public String getFieldTip(String fieldName, Locale locale)
300                    throws PortalException, SystemException {
301    
302                    return getFieldTip(fieldName, LocaleUtil.toLanguageId(locale));
303            }
304    
305            @Override
306            public String getFieldTip(String fieldName, String locale)
307                    throws PortalException, SystemException {
308    
309                    return GetterUtil.getString(
310                            getFieldProperty(fieldName, "tip", locale), fieldName);
311            }
312    
313            @Override
314            public String getFieldType(String fieldName)
315                    throws PortalException, SystemException {
316    
317                    return getFieldProperty(fieldName, "type");
318            }
319    
320            @Override
321            public Map<String, Map<String, Map<String, String>>>
322                    getLocalizedFieldsMap() {
323    
324                    if (_localizedFieldsMap == null) {
325                            _localizedFieldsMap =
326                                    new ConcurrentHashMap
327                                            <String, Map<String, Map<String, String>>>();
328                    }
329    
330                    return _localizedFieldsMap;
331            }
332    
333            @Override
334            public Map<String, Map<String, Map<String, String>>>
335                    getLocalizedPersistentFieldsMap() {
336    
337                    if (_localizedPersistentFieldsMap == null) {
338                            _localizedPersistentFieldsMap =
339                                    new ConcurrentHashMap
340                                            <String, Map<String, Map<String, String>>>();
341                    }
342    
343                    return _localizedPersistentFieldsMap;
344            }
345    
346            @Override
347            public Map<String, Map<String, Map<String, String>>>
348                    getLocalizedTransientFieldsMap() {
349    
350                    if (_localizedTransientFieldsMap == null) {
351                            _localizedTransientFieldsMap =
352                                    new ConcurrentHashMap
353                                            <String, Map<String, Map<String, String>>>();
354                    }
355    
356                    return _localizedTransientFieldsMap;
357            }
358    
359            @Override
360            public Map<String, Map<String, String>> getPersistentFieldsMap(
361                            String locale)
362                    throws PortalException, SystemException {
363    
364                    _indexFieldsMap(locale);
365    
366                    Map<String, Map<String, Map<String, String>>>
367                            localizedPersistentFieldsMap = getLocalizedPersistentFieldsMap();
368    
369                    Map<String, Map<String, String>> fieldsMap =
370                            localizedPersistentFieldsMap.get(locale);
371    
372                    return fieldsMap;
373            }
374    
375            @Override
376            public List<String> getRootFieldNames()
377                    throws PortalException, SystemException {
378    
379                    List<String> fieldNames = new ArrayList<String>();
380    
381                    Map<String, Map<String, String>> fieldsMap = getFieldsMap(true);
382    
383                    for (Map.Entry<String, Map<String, String>> entry :
384                                    fieldsMap.entrySet()) {
385    
386                            Map<String, String> field = entry.getValue();
387    
388                            String parentNameKey = _getPrivateAttributeKey("parentName");
389    
390                            if (!field.containsKey(parentNameKey)) {
391                                    fieldNames.add(entry.getKey());
392                            }
393                    }
394    
395                    return fieldNames;
396            }
397    
398            @Override
399            public List<DDMTemplate> getTemplates() throws SystemException {
400                    return DDMTemplateLocalServiceUtil.getTemplates(getStructureId());
401            }
402    
403            @Override
404            public Map<String, Map<String, String>> getTransientFieldsMap(String locale)
405                    throws PortalException, SystemException {
406    
407                    _indexFieldsMap(locale);
408    
409                    Map<String, Map<String, Map<String, String>>>
410                            localizedTransientFieldsMap = getLocalizedTransientFieldsMap();
411    
412                    Map<String, Map<String, String>> fieldsMap =
413                            localizedTransientFieldsMap.get(locale);
414    
415                    return fieldsMap;
416            }
417    
418            /**
419             * Returns the WebDAV URL to access the structure.
420             *
421             * @param  themeDisplay the theme display needed to build the URL. It can
422             *         set HTTPS access, the server name, the server port, the path
423             *         context, and the scope group.
424             * @param  webDAVToken the WebDAV token for the URL
425             * @return the WebDAV URL
426             */
427            @Override
428            public String getWebDavURL(ThemeDisplay themeDisplay, String webDAVToken) {
429                    StringBundler sb = new StringBundler(11);
430    
431                    boolean secure = false;
432    
433                    if (themeDisplay.isSecure() ||
434                            PropsValues.WEBDAV_SERVLET_HTTPS_REQUIRED) {
435    
436                            secure = true;
437                    }
438    
439                    String portalURL = PortalUtil.getPortalURL(
440                            themeDisplay.getServerName(), themeDisplay.getServerPort(), secure);
441    
442                    sb.append(portalURL);
443    
444                    sb.append(themeDisplay.getPathContext());
445                    sb.append(StringPool.SLASH);
446                    sb.append("webdav");
447    
448                    Group group = themeDisplay.getScopeGroup();
449    
450                    sb.append(group.getFriendlyURL());
451    
452                    sb.append(StringPool.SLASH);
453                    sb.append(webDAVToken);
454                    sb.append(StringPool.SLASH);
455                    sb.append("Structures");
456                    sb.append(StringPool.SLASH);
457                    sb.append(getStructureId());
458    
459                    return sb.toString();
460            }
461    
462            @Override
463            public boolean hasField(String fieldName)
464                    throws PortalException, SystemException {
465    
466                    Map<String, Map<String, String>> fieldsMap = getFieldsMap(true);
467    
468                    if (fieldsMap.containsKey(fieldName)) {
469                            return true;
470                    }
471    
472                    try {
473                            DDMStructure parentStructure =
474                                    DDMStructureLocalServiceUtil.getStructure(
475                                            getParentStructureId());
476    
477                            return parentStructure.hasField(fieldName);
478                    }
479                    catch (NoSuchStructureException nsse) {
480                            return false;
481                    }
482            }
483    
484            @Override
485            public boolean isFieldPrivate(String fieldName)
486                    throws PortalException, SystemException {
487    
488                    return GetterUtil.getBoolean(getFieldProperty(fieldName, "private"));
489            }
490    
491            @Override
492            public boolean isFieldRepeatable(String fieldName)
493                    throws PortalException, SystemException {
494    
495                    return GetterUtil.getBoolean(getFieldProperty(fieldName, "repeatable"));
496            }
497    
498            @Override
499            public boolean isFieldTransient(String fieldName)
500                    throws PortalException, SystemException {
501    
502                    if (!hasField(fieldName)) {
503                            throw new StructureFieldException();
504                    }
505    
506                    Map<String, Map<String, String>> transientFieldsMap =
507                            getTransientFieldsMap(getDefaultLanguageId());
508    
509                    return transientFieldsMap.containsKey(fieldName);
510            }
511    
512            @Override
513            public void prepareLocalizedFieldsForImport(Locale defaultImportLocale)
514                    throws LocaleException {
515    
516                    super.prepareLocalizedFieldsForImport(defaultImportLocale);
517    
518                    Locale ddmStructureDefaultLocale = LocaleUtil.fromLanguageId(
519                            getDefaultLanguageId());
520    
521                    try {
522                            setXsd(
523                                    DDMXMLUtil.updateXMLDefaultLocale(
524                                            getXsd(), ddmStructureDefaultLocale, defaultImportLocale));
525                    }
526                    catch (Exception e) {
527                            throw new LocaleException(LocaleException.TYPE_EXPORT_IMPORT, e);
528                    }
529            }
530    
531            @Override
532            public void setDocument(Document document) {
533                    _document = document;
534            }
535    
536            @Override
537            public void setLocalizedFieldsMap(
538                    Map<String, Map<String, Map<String, String>>> localizedFieldsMap) {
539    
540                    _localizedFieldsMap = localizedFieldsMap;
541            }
542    
543            @Override
544            public void setLocalizedPersistentFieldsMap(
545                    Map<String, Map<String, Map<String, String>>>
546                            localizedPersistentFieldsMap) {
547    
548                    _localizedPersistentFieldsMap = localizedPersistentFieldsMap;
549            }
550    
551            @Override
552            public void setLocalizedTransientFieldsMap(
553                    Map<String, Map<String, Map<String, String>>>
554                            localizedTransientFieldsMap) {
555    
556                    _localizedTransientFieldsMap = localizedTransientFieldsMap;
557            }
558    
559            @Override
560            public void setXsd(String xsd) {
561                    super.setXsd(xsd);
562    
563                    _document = null;
564                    _localizedFieldsMap = null;
565                    _localizedPersistentFieldsMap = null;
566                    _localizedTransientFieldsMap = null;
567            }
568    
569            private Map<String, String> _getField(Element element, String locale) {
570                    Map<String, String> field = new HashMap<String, String>();
571    
572                    String[] availableLanguageIds = getAvailableLanguageIds();
573    
574                    if ((locale != null) &&
575                            !ArrayUtil.contains(availableLanguageIds, locale)) {
576    
577                            locale = getDefaultLanguageId();
578                    }
579    
580                    locale = HtmlUtil.escapeXPathAttribute(locale);
581    
582                    String xPathExpression =
583                            "meta-data[@locale=".concat(locale).concat("]");
584    
585                    XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
586    
587                    Node node = xPathSelector.selectSingleNode(element);
588    
589                    Element metaDataElement = (Element)node.asXPathResult(node.getParent());
590    
591                    if (metaDataElement != null) {
592                            List<Element> childMetaDataElements = metaDataElement.elements();
593    
594                            for (Element childMetaDataElement : childMetaDataElements) {
595                                    String name = childMetaDataElement.attributeValue("name");
596                                    String value = childMetaDataElement.getText();
597    
598                                    field.put(name, value);
599                            }
600                    }
601    
602                    for (Attribute attribute : element.attributes()) {
603                            field.put(attribute.getName(), attribute.getValue());
604                    }
605    
606                    Element parentElement = element.getParent();
607    
608                    if (parentElement != null) {
609                            String parentName = parentElement.attributeValue("name");
610    
611                            if (Validator.isNotNull(parentName)) {
612                                    field.put(_getPrivateAttributeKey("parentName"), parentName);
613                            }
614                    }
615    
616                    return field;
617            }
618    
619            private String _getPrivateAttributeKey(String attributeName) {
620                    return StringPool.UNDERLINE.concat(attributeName).concat(
621                            StringPool.UNDERLINE);
622            }
623    
624            private Map<String, String> _getPrivateField(String privateFieldName) {
625                    Map<String, String> privateField = new HashMap<String, String>();
626    
627                    String dataType = PropsUtil.get(
628                            PropsKeys.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_DATATYPE,
629                            new Filter(privateFieldName));
630    
631                    privateField.put("dataType", dataType);
632    
633                    privateField.put("name", privateFieldName);
634                    privateField.put("private", Boolean.TRUE.toString());
635    
636                    String repeatable = PropsUtil.get(
637                            PropsKeys.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_REPEATABLE,
638                            new Filter(privateFieldName));
639    
640                    privateField.put("repeatable", repeatable);
641    
642                    return privateField;
643            }
644    
645            private void _indexFieldsMap(String locale)
646                    throws PortalException, SystemException {
647    
648                    Map<String, Map<String, Map<String, String>>> localizedFieldsMap =
649                            getLocalizedFieldsMap();
650    
651                    Map<String, Map<String, String>> fieldsMap = localizedFieldsMap.get(
652                            locale);
653    
654                    Map<String, Map<String, Map<String, String>>>
655                            localizedPersistentFieldsMap = getLocalizedPersistentFieldsMap();
656    
657                    Map<String, Map<String, String>> persistentFieldsMap =
658                            localizedPersistentFieldsMap.get(locale);
659    
660                    Map<String, Map<String, Map<String, String>>>
661                            localizedTransientFieldsMap = getLocalizedTransientFieldsMap();
662    
663                    Map<String, Map<String, String>> transientFieldsMap =
664                            localizedTransientFieldsMap.get(locale);
665    
666                    if (fieldsMap != null) {
667                            return;
668                    }
669    
670                    fieldsMap = new LinkedHashMap<String, Map<String, String>>();
671                    persistentFieldsMap = new LinkedHashMap<String, Map<String, String>>();
672                    transientFieldsMap = new LinkedHashMap<String, Map<String, String>>();
673    
674                    if (getParentStructureId() > 0) {
675                            DDMStructure parentStructure =
676                                    DDMStructureLocalServiceUtil.getStructure(
677                                            getParentStructureId());
678    
679                            fieldsMap.putAll(parentStructure.getFieldsMap(locale, true));
680                            persistentFieldsMap.putAll(
681                                    parentStructure.getPersistentFieldsMap(locale));
682                            transientFieldsMap.putAll(
683                                    parentStructure.getTransientFieldsMap(locale));
684                    }
685    
686                    XPath xPathSelector = SAXReaderUtil.createXPath("//dynamic-element");
687    
688                    List<Node> nodes = xPathSelector.selectNodes(getDocument());
689    
690                    for (Node node : nodes) {
691                            Element element = (Element)node;
692    
693                            String name = element.attributeValue("name");
694    
695                            fieldsMap.put(name, _getField(element, locale));
696    
697                            if (Validator.isNotNull(element.attributeValue("dataType"))) {
698                                    persistentFieldsMap.put(name, _getField(element, locale));
699                            }
700                            else {
701                                    transientFieldsMap.put(name, _getField(element, locale));
702                            }
703                    }
704    
705                    String[] privateFieldNames =
706                            PropsValues.DYNAMIC_DATA_MAPPING_STRUCTURE_PRIVATE_FIELD_NAMES;
707    
708                    for (String privateFieldName : privateFieldNames) {
709                            Map<String, String> privateField = _getPrivateField(
710                                    privateFieldName);
711    
712                            fieldsMap.put(privateFieldName, privateField);
713                            persistentFieldsMap.put(privateFieldName, privateField);
714                    }
715    
716                    localizedFieldsMap.put(locale, fieldsMap);
717                    localizedPersistentFieldsMap.put(locale, persistentFieldsMap);
718                    localizedTransientFieldsMap.put(locale, transientFieldsMap);
719            }
720    
721            private String _mergeXsds(String xsd1, String xsd2) throws SystemException {
722                    try {
723                            Document document1 = SAXReaderUtil.read(xsd1);
724                            Document document2 = SAXReaderUtil.read(xsd2);
725    
726                            Element rootElement1 = document1.getRootElement();
727                            Element rootElement2 = document2.getRootElement();
728    
729                            for (Element element : rootElement1.elements()) {
730                                    rootElement1.remove(element);
731    
732                                    rootElement2.add(element);
733                            }
734    
735                            return rootElement2.formattedString();
736                    }
737                    catch (Exception e) {
738                            throw new SystemException(e);
739                    }
740            }
741    
742            private static Log _log = LogFactoryUtil.getLog(DDMStructureImpl.class);
743    
744            @CacheField
745            private Document _document;
746    
747            @CacheField
748            private Map<String, Map<String, Map<String, String>>> _localizedFieldsMap;
749    
750            @CacheField
751            private Map<String, Map<String, Map<String, String>>>
752                    _localizedPersistentFieldsMap;
753    
754            @CacheField
755            private Map<String, Map<String, Map<String, String>>>
756                    _localizedTransientFieldsMap;
757    
758    }