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