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