001    /**
002     * Copyright (c) 2000-present 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.portal.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
019    import com.liferay.portal.kernel.json.JSONObject;
020    import com.liferay.portal.kernel.language.LanguageUtil;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
024    import com.liferay.portal.kernel.settings.LocalizedValuesMap;
025    import com.liferay.portal.kernel.settings.Settings;
026    import com.liferay.portal.kernel.util.ArrayUtil;
027    import com.liferay.portal.kernel.util.ClassLoaderUtil;
028    import com.liferay.portal.kernel.util.LocaleUtil;
029    import com.liferay.portal.kernel.util.Localization;
030    import com.liferay.portal.kernel.util.ParamUtil;
031    import com.liferay.portal.kernel.util.PrefsParamUtil;
032    import com.liferay.portal.kernel.util.ResourceBundleUtil;
033    import com.liferay.portal.kernel.util.StringBundler;
034    import com.liferay.portal.kernel.util.StringPool;
035    import com.liferay.portal.kernel.util.StringUtil;
036    import com.liferay.portal.kernel.util.Tuple;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.kernel.xml.Document;
039    import com.liferay.portal.kernel.xml.Element;
040    import com.liferay.portal.language.LanguageResources;
041    import com.liferay.portal.security.xml.SecureXMLFactoryProviderUtil;
042    import com.liferay.util.ContentUtil;
043    
044    import java.util.Collection;
045    import java.util.HashMap;
046    import java.util.Locale;
047    import java.util.Map;
048    import java.util.ResourceBundle;
049    import java.util.Set;
050    
051    import javax.portlet.PortletPreferences;
052    import javax.portlet.PortletRequest;
053    
054    import javax.servlet.http.HttpServletRequest;
055    
056    import javax.xml.stream.XMLInputFactory;
057    import javax.xml.stream.XMLOutputFactory;
058    import javax.xml.stream.XMLStreamConstants;
059    import javax.xml.stream.XMLStreamException;
060    import javax.xml.stream.XMLStreamReader;
061    import javax.xml.stream.XMLStreamWriter;
062    
063    import org.apache.commons.collections.map.ReferenceMap;
064    
065    /**
066     * @author Alexander Chow
067     * @author Jorge Ferrer
068     * @author Mauro Mariuzzo
069     * @author Julio Camarero
070     * @author Brian Wing Shun Chan
071     * @author Connor McKay
072     */
073    @DoPrivileged
074    public class LocalizationImpl implements Localization {
075    
076            @Override
077            public Object deserialize(JSONObject jsonObject) {
078                    Map<Locale, String> map = new HashMap<>();
079    
080                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
081                            String languageId = LocaleUtil.toLanguageId(locale);
082    
083                            String value = jsonObject.getString(languageId);
084    
085                            if (Validator.isNotNull(value)) {
086                                    map.put(locale, value);
087                            }
088                    }
089    
090                    return map;
091            }
092    
093            @Override
094            public String[] getAvailableLanguageIds(Document document) {
095                    String attributeValue = _getRootAttributeValue(
096                            document, _AVAILABLE_LOCALES, StringPool.BLANK);
097    
098                    return StringUtil.split(attributeValue);
099            }
100    
101            @Override
102            public String[] getAvailableLanguageIds(String xml) {
103                    String attributeValue = _getRootAttributeValue(
104                            xml, _AVAILABLE_LOCALES, StringPool.BLANK);
105    
106                    return StringUtil.split(attributeValue);
107            }
108    
109            @Override
110            public Locale getDefaultImportLocale(
111                    String className, long classPK, Locale contentDefaultLocale,
112                    Locale[] contentAvailableLocales) {
113    
114                    if (LanguageUtil.isAvailableLocale(contentDefaultLocale)) {
115                            return contentDefaultLocale;
116                    }
117    
118                    Locale defaultLocale = LocaleUtil.getSiteDefault();
119    
120                    if (ArrayUtil.contains(contentAvailableLocales, defaultLocale)) {
121                            return defaultLocale;
122                    }
123    
124                    for (Locale contentAvailableLocale : contentAvailableLocales) {
125                            if (LanguageUtil.isAvailableLocale(contentAvailableLocale)) {
126                                    return contentAvailableLocale;
127                            }
128                    }
129    
130                    if (_log.isWarnEnabled()) {
131                            StringBundler sb = new StringBundler(9);
132    
133                            sb.append("Language ");
134                            sb.append(LocaleUtil.toLanguageId(contentDefaultLocale));
135                            sb.append(" is missing for ");
136                            sb.append(className);
137                            sb.append(" with primary key ");
138                            sb.append(classPK);
139                            sb.append(". Setting default language to ");
140                            sb.append(LocaleUtil.toLanguageId(defaultLocale));
141                            sb.append(".");
142    
143                            _log.warn(sb.toString());
144                    }
145    
146                    return defaultLocale;
147            }
148    
149            @Override
150            public String getDefaultLanguageId(Document document) {
151                    return getDefaultLanguageId(document, LocaleUtil.getSiteDefault());
152            }
153    
154            @Override
155            public String getDefaultLanguageId(
156                    Document document, Locale defaultLocale) {
157    
158                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
159    
160                    return _getRootAttributeValue(
161                            document, _DEFAULT_LOCALE, defaultLanguageId);
162            }
163    
164            @Override
165            public String getDefaultLanguageId(String xml) {
166                    return getDefaultLanguageId(xml, LocaleUtil.getSiteDefault());
167            }
168    
169            @Override
170            public String getDefaultLanguageId(String xml, Locale defaultLocale) {
171                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
172    
173                    return _getRootAttributeValue(xml, _DEFAULT_LOCALE, defaultLanguageId);
174            }
175    
176            @Override
177            public String getLocalization(String xml, String requestedLanguageId) {
178                    return getLocalization(xml, requestedLanguageId, true);
179            }
180    
181            @Override
182            public String getLocalization(
183                    String xml, String requestedLanguageId, boolean useDefault) {
184    
185                    return getLocalization(
186                            xml, requestedLanguageId, useDefault, StringPool.BLANK);
187            }
188    
189            @Override
190            public String getLocalization(
191                    String xml, String requestedLanguageId, boolean useDefault,
192                    String defaultValue) {
193    
194                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
195                            LocaleUtil.getSiteDefault());
196    
197                    if (!Validator.isXml(xml)) {
198                            if (useDefault ||
199                                    requestedLanguageId.equals(systemDefaultLanguageId)) {
200    
201                                    return xml;
202                            }
203                            else {
204                                    return defaultValue;
205                            }
206                    }
207    
208                    String value = _getCachedValue(xml, requestedLanguageId, useDefault);
209    
210                    if (value != null) {
211                            return value;
212                    }
213    
214                    String priorityLanguageId = null;
215    
216                    Locale requestedLocale = LocaleUtil.fromLanguageId(requestedLanguageId);
217    
218                    if (useDefault &&
219                            LanguageUtil.isDuplicateLanguageCode(
220                                    requestedLocale.getLanguage())) {
221    
222                            Locale priorityLocale = LanguageUtil.getLocale(
223                                    requestedLocale.getLanguage());
224    
225                            if (!requestedLanguageId.equals(priorityLanguageId)) {
226                                    priorityLanguageId = LocaleUtil.toLanguageId(priorityLocale);
227                            }
228                    }
229    
230                    XMLStreamReader xmlStreamReader = null;
231    
232                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
233    
234                    ClassLoader contextClassLoader =
235                            ClassLoaderUtil.getContextClassLoader();
236    
237                    try {
238                            if (contextClassLoader != portalClassLoader) {
239                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
240                            }
241    
242                            XMLInputFactory xmlInputFactory =
243                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
244    
245                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
246                                    new UnsyncStringReader(xml));
247    
248                            String defaultLanguageId = StringPool.BLANK;
249    
250                            // Skip root node
251    
252                            if (xmlStreamReader.hasNext()) {
253                                    xmlStreamReader.nextTag();
254    
255                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
256                                            null, _DEFAULT_LOCALE);
257    
258                                    if (Validator.isNull(defaultLanguageId)) {
259                                            defaultLanguageId = systemDefaultLanguageId;
260                                    }
261                            }
262    
263                            // Find specified language and/or default language
264    
265                            String priorityValue = null;
266                            String defaultLocalizationValue = null;
267    
268                            while (xmlStreamReader.hasNext()) {
269                                    int event = xmlStreamReader.next();
270    
271                                    if (event == XMLStreamConstants.START_ELEMENT) {
272                                            String languageId = xmlStreamReader.getAttributeValue(
273                                                    null, _LANGUAGE_ID);
274    
275                                            if (Validator.isNull(languageId)) {
276                                                    languageId = defaultLanguageId;
277                                            }
278    
279                                            if (languageId.equals(defaultLanguageId) ||
280                                                    languageId.equals(priorityLanguageId) ||
281                                                    languageId.equals(requestedLanguageId)) {
282    
283                                                    String text = xmlStreamReader.getElementText();
284    
285                                                    if (languageId.equals(defaultLanguageId)) {
286                                                            defaultLocalizationValue = text;
287                                                    }
288    
289                                                    if (languageId.equals(priorityLanguageId)) {
290                                                            priorityValue = text;
291                                                    }
292    
293                                                    if (languageId.equals(requestedLanguageId)) {
294                                                            value = text;
295                                                    }
296    
297                                                    if (Validator.isNotNull(value)) {
298                                                            break;
299                                                    }
300                                            }
301                                    }
302                                    else if (event == XMLStreamConstants.END_DOCUMENT) {
303                                            break;
304                                    }
305                            }
306    
307                            if (useDefault && Validator.isNotNull(priorityLanguageId) &&
308                                    Validator.isNull(value) && Validator.isNotNull(priorityValue)) {
309    
310                                    value = priorityValue;
311                            }
312    
313                            if (useDefault && Validator.isNull(value)) {
314                                    value = defaultLocalizationValue;
315                            }
316    
317                            if (Validator.isNull(value)) {
318                                    value = defaultValue;
319                            }
320                    }
321                    catch (Exception e) {
322                            if (_log.isWarnEnabled()) {
323                                    _log.warn(e, e);
324                            }
325                    }
326                    finally {
327                            if (contextClassLoader != portalClassLoader) {
328                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
329                            }
330    
331                            if (xmlStreamReader != null) {
332                                    try {
333                                            xmlStreamReader.close();
334                                    }
335                                    catch (Exception e) {
336                                    }
337                            }
338                    }
339    
340                    _setCachedValue(xml, requestedLanguageId, useDefault, value);
341    
342                    return value;
343            }
344    
345            @Override
346            public Map<Locale, String> getLocalizationMap(
347                    Collection<Locale> locales, Locale defaultLocale, String key) {
348    
349                    Map<Locale, String> map = new HashMap<>();
350    
351                    String defaultValue = LanguageUtil.get(defaultLocale, key);
352    
353                    for (Locale locale : locales) {
354                            String value = LanguageUtil.get(locale, key);
355    
356                            if (!locale.equals(defaultLocale) && value.equals(defaultValue)) {
357                                    continue;
358                            }
359    
360                            map.put(locale, value);
361                    }
362    
363                    return map;
364            }
365    
366            @Override
367            public Map<Locale, String> getLocalizationMap(
368                    HttpServletRequest request, String parameter) {
369    
370                    Map<Locale, String> map = new HashMap<>();
371    
372                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
373                            String localizedParameter = getLocalizedName(
374                                    parameter, LocaleUtil.toLanguageId(locale));
375    
376                            map.put(locale, ParamUtil.getString(request, localizedParameter));
377                    }
378    
379                    return map;
380            }
381    
382            @Override
383            public Map<Locale, String> getLocalizationMap(
384                    PortletPreferences preferences, String preferenceName) {
385    
386                    return getLocalizationMap(preferences, preferenceName, null);
387            }
388    
389            @Override
390            public Map<Locale, String> getLocalizationMap(
391                    PortletPreferences preferences, String preferenceName,
392                    String propertyName) {
393    
394                    String defaultPropertyValue = null;
395    
396                    if (propertyName != null) {
397                            defaultPropertyValue = PropsUtil.get(propertyName);
398                    }
399    
400                    return getLocalizationMap(
401                            preferences, preferenceName, propertyName, defaultPropertyValue,
402                            getClass().getClassLoader());
403            }
404    
405            @Override
406            public Map<Locale, String> getLocalizationMap(
407                    PortletPreferences preferences, String preferenceName,
408                    String propertyName, String defaultPropertyValue,
409                    ClassLoader classLoader) {
410    
411                    Map<Locale, String> map = new HashMap<>();
412    
413                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
414                            String localizedPreference = getLocalizedName(
415                                    preferenceName, LocaleUtil.toLanguageId(locale));
416    
417                            map.put(
418                                    locale,
419                                    preferences.getValue(localizedPreference, StringPool.BLANK));
420                    }
421    
422                    if (Validator.isNull(propertyName)) {
423                            return map;
424                    }
425    
426                    Locale defaultLocale = LocaleUtil.getSiteDefault();
427    
428                    String defaultValue = map.get(defaultLocale);
429    
430                    if (Validator.isNotNull(defaultValue)) {
431                            return map;
432                    }
433    
434                    map.put(
435                            defaultLocale, ContentUtil.get(classLoader, defaultPropertyValue));
436    
437                    return map;
438            }
439    
440            @Override
441            public Map<Locale, String> getLocalizationMap(
442                    PortletRequest portletRequest, String parameter) {
443    
444                    return getLocalizationMap(
445                            portletRequest, parameter, new HashMap<Locale, String>());
446            }
447    
448            @Override
449            public Map<Locale, String> getLocalizationMap(
450                    PortletRequest portletRequest, String parameter,
451                    Map<Locale, String> defaultValues) {
452    
453                    Map<Locale, String> map = new HashMap<>();
454    
455                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
456                            String localizedParameter = getLocalizedName(
457                                    parameter, LocaleUtil.toLanguageId(locale));
458    
459                            map.put(
460                                    locale,
461                                    ParamUtil.getString(
462                                            portletRequest, localizedParameter,
463                                            defaultValues.get(locale)));
464                    }
465    
466                    return map;
467            }
468    
469            @Override
470            public Map<Locale, String> getLocalizationMap(String xml) {
471                    return getLocalizationMap(xml, false);
472            }
473    
474            @Override
475            public Map<Locale, String> getLocalizationMap(
476                    String xml, boolean useDefault) {
477    
478                    Map<Locale, String> map = new HashMap<>();
479    
480                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
481                            String languageId = LocaleUtil.toLanguageId(locale);
482    
483                            String value = getLocalization(xml, languageId, useDefault);
484    
485                            if (Validator.isNotNull(value)) {
486                                    map.put(locale, value);
487                            }
488                    }
489    
490                    return map;
491            }
492    
493            @Override
494            public Map<Locale, String> getLocalizationMap(
495                    String bundleName, ClassLoader classLoader, String key,
496                    boolean includeBetaLocales) {
497    
498                    if (key == null) {
499                            return null;
500                    }
501    
502                    Map<Locale, String> map = new HashMap<>();
503    
504                    Locale defaultLocale = LocaleUtil.getSiteDefault();
505    
506                    String defaultValue = _getLocalization(
507                            bundleName, defaultLocale, classLoader, key, key);
508    
509                    map.put(defaultLocale, defaultValue);
510    
511                    Set<Locale> locales = null;
512    
513                    if (includeBetaLocales) {
514                            locales = LanguageUtil.getAvailableLocales();
515                    }
516                    else {
517                            locales = LanguageUtil.getSupportedLocales();
518                    }
519    
520                    for (Locale locale : locales) {
521                            if (locale.equals(defaultLocale)) {
522                                    continue;
523                            }
524    
525                            String value = _getLocalization(
526                                    bundleName, locale, classLoader, key, null);
527    
528                            if (Validator.isNotNull(value) && !value.equals(defaultValue)) {
529                                    map.put(locale, value);
530                            }
531                    }
532    
533                    return map;
534            }
535    
536            @Override
537            public Map<Locale, String> getLocalizationMap(
538                    String[] languageIds, String[] values) {
539    
540                    Map<Locale, String> map = new HashMap<>();
541    
542                    for (int i = 0; i < values.length; i++) {
543                            Locale locale = LocaleUtil.fromLanguageId(languageIds[i]);
544    
545                            map.put(locale, values[i]);
546                    }
547    
548                    return map;
549            }
550    
551            @Override
552            public String getLocalizationXmlFromPreferences(
553                    PortletPreferences preferences, PortletRequest portletRequest,
554                    String parameter) {
555    
556                    return getLocalizationXmlFromPreferences(
557                            preferences, portletRequest, parameter, null, null);
558            }
559    
560            @Override
561            public String getLocalizationXmlFromPreferences(
562                    PortletPreferences preferences, PortletRequest portletRequest,
563                    String parameter, String defaultValue) {
564    
565                    return getLocalizationXmlFromPreferences(
566                            preferences, portletRequest, parameter, defaultValue, null);
567            }
568    
569            @Override
570            public String getLocalizationXmlFromPreferences(
571                    PortletPreferences preferences, PortletRequest portletRequest,
572                    String parameter, String prefix, String defaultValue) {
573    
574                    String xml = null;
575    
576                    Locale defaultLocale = LocaleUtil.getSiteDefault();
577                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
578    
579                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
580                            String languageId = LocaleUtil.toLanguageId(locale);
581    
582                            String localizedKey = getLocalizedName(parameter, languageId);
583    
584                            String prefixedLocalizedKey = localizedKey;
585    
586                            if (Validator.isNotNull(prefix)) {
587                                    prefixedLocalizedKey = prefix + "--" + localizedKey + "--";
588                            }
589    
590                            String value = ParamUtil.getString(
591                                    portletRequest, prefixedLocalizedKey,
592                                    preferences.getValue(localizedKey, null));
593    
594                            if (value != null) {
595                                    xml = updateLocalization(xml, parameter, value, languageId);
596                            }
597                    }
598    
599                    if (getLocalization(xml, defaultLanguageId, true, null) == null) {
600                            String oldValue = PrefsParamUtil.getString(
601                                    preferences, portletRequest, parameter, defaultValue);
602    
603                            if (Validator.isNotNull(oldValue)) {
604                                    xml = updateLocalization(xml, parameter, oldValue);
605                            }
606                    }
607    
608                    return xml;
609            }
610    
611            @Override
612            public String getLocalizedName(String name, String languageId) {
613                    return name.concat(StringPool.UNDERLINE).concat(languageId);
614            }
615    
616            /**
617             * @deprecated As of 7.0.0
618             */
619            @Deprecated
620            @Override
621            public Map<Locale, String> getLocalizedParameter(
622                    PortletRequest portletRequest, String parameter) {
623    
624                    return getLocalizationMap(portletRequest, parameter);
625            }
626    
627            @Override
628            public Map<Locale, String> getMap(LocalizedValuesMap localizedValuesMap) {
629                    Map<Locale, String> map = localizedValuesMap.getValues();
630    
631                    Locale locale = LocaleUtil.getDefault();
632    
633                    if (map.get(locale) == null) {
634                            map.put(locale, localizedValuesMap.getDefaultValue());
635                    }
636    
637                    return map;
638            }
639    
640            /**
641             * @deprecated As of 7.0.0
642             */
643            @Deprecated
644            @Override
645            public String getPreferencesKey(String key, String languageId) {
646                    String defaultLanguageId = LocaleUtil.toLanguageId(
647                            LocaleUtil.getSiteDefault());
648    
649                    if (!languageId.equals(defaultLanguageId)) {
650                            key = getLocalizedName(key, languageId);
651                    }
652    
653                    return key;
654            }
655    
656            @Override
657            public String getPreferencesValue(
658                    PortletPreferences preferences, String key, String languageId) {
659    
660                    return getPreferencesValue(preferences, key, languageId, true);
661            }
662    
663            @Override
664            public String getPreferencesValue(
665                    PortletPreferences preferences, String key, String languageId,
666                    boolean useDefault) {
667    
668                    String localizedKey = getLocalizedName(key, languageId);
669    
670                    String value = preferences.getValue(localizedKey, StringPool.BLANK);
671    
672                    if (useDefault && Validator.isNull(value)) {
673                            value = preferences.getValue(
674                                    _getDefaultLocalizedName(key), StringPool.BLANK);
675    
676                            if (Validator.isNull(value)) {
677                                    value = preferences.getValue(key, StringPool.BLANK);
678                            }
679                    }
680    
681                    return value;
682            }
683    
684            @Override
685            public String[] getPreferencesValues(
686                    PortletPreferences preferences, String key, String languageId) {
687    
688                    return getPreferencesValues(preferences, key, languageId, true);
689            }
690    
691            @Override
692            public String[] getPreferencesValues(
693                    PortletPreferences preferences, String key, String languageId,
694                    boolean useDefault) {
695    
696                    String localizedKey = getLocalizedName(key, languageId);
697    
698                    String[] values = preferences.getValues(localizedKey, new String[0]);
699    
700                    if (useDefault && ArrayUtil.isEmpty(values)) {
701                            values = preferences.getValues(
702                                    _getDefaultLocalizedName(key), new String[0]);
703    
704                            if (ArrayUtil.isEmpty(values)) {
705                                    values = preferences.getValues(key, new String[0]);
706                            }
707                    }
708    
709                    return values;
710            }
711    
712            @Override
713            public String getSettingsValue(
714                    Settings settings, String key, String languageId) {
715    
716                    return getSettingsValue(settings, key, languageId, true);
717            }
718    
719            @Override
720            public String getSettingsValue(
721                    Settings settings, String key, String languageId, boolean useDefault) {
722    
723                    String localizedKey = getLocalizedName(key, languageId);
724    
725                    String value = settings.getValue(localizedKey, StringPool.BLANK);
726    
727                    if (useDefault && Validator.isNull(value)) {
728                            value = settings.getValue(
729                                    _getDefaultLocalizedName(key), StringPool.BLANK);
730    
731                            if (Validator.isNull(value)) {
732                                    value = settings.getValue(key, StringPool.BLANK);
733                            }
734                    }
735    
736                    return value;
737            }
738    
739            @Override
740            public String[] getSettingsValues(
741                    Settings settings, String key, String languageId) {
742    
743                    return getSettingsValues(settings, key, languageId, true);
744            }
745    
746            @Override
747            public String[] getSettingsValues(
748                    Settings settings, String key, String languageId, boolean useDefault) {
749    
750                    String localizedKey = getLocalizedName(key, languageId);
751    
752                    String[] values = settings.getValues(localizedKey, new String[0]);
753    
754                    if (useDefault && ArrayUtil.isEmpty(values)) {
755                            values = settings.getValues(
756                                    _getDefaultLocalizedName(key), new String[0]);
757    
758                            if (ArrayUtil.isEmpty(values)) {
759                                    values = settings.getValues(key, new String[0]);
760                            }
761                    }
762    
763                    return values;
764            }
765    
766            @Override
767            public String getXml(LocalizedValuesMap localizedValuesMap, String key) {
768                    XMLStreamWriter xmlStreamWriter = null;
769    
770                    try {
771                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
772    
773                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
774    
775                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
776                                    unsyncStringWriter);
777    
778                            xmlStreamWriter.writeStartDocument();
779    
780                            xmlStreamWriter.writeStartElement("root");
781    
782                            Set<Locale> availableLocales = LanguageUtil.getAvailableLocales();
783    
784                            xmlStreamWriter.writeAttribute(
785                                    "available-locales", StringUtil.merge(availableLocales));
786    
787                            Locale defaultLocale = LocaleUtil.getSiteDefault();
788    
789                            xmlStreamWriter.writeAttribute(
790                                    "default-locale", defaultLocale.toString());
791    
792                            for (Locale locale : availableLocales) {
793                                    String value = localizedValuesMap.get(locale);
794    
795                                    if (value != null) {
796                                            xmlStreamWriter.writeStartElement(key);
797    
798                                            xmlStreamWriter.writeAttribute(
799                                                    "language-id", locale.toString());
800    
801                                            xmlStreamWriter.writeCharacters(value);
802    
803                                            xmlStreamWriter.writeEndElement();
804                                    }
805                            }
806    
807                            xmlStreamWriter.writeEndElement();
808    
809                            xmlStreamWriter.writeEndDocument();
810    
811                            return unsyncStringWriter.toString();
812                    }
813                    catch (XMLStreamException xmlse) {
814                            throw new RuntimeException(xmlse);
815                    }
816                    finally {
817                            _close(xmlStreamWriter);
818                    }
819            }
820    
821            @Override
822            public String removeLocalization(
823                    String xml, String key, String requestedLanguageId) {
824    
825                    return removeLocalization(xml, key, requestedLanguageId, false);
826            }
827    
828            @Override
829            public String removeLocalization(
830                    String xml, String key, String requestedLanguageId, boolean cdata) {
831    
832                    return removeLocalization(xml, key, requestedLanguageId, cdata, true);
833            }
834    
835            @Override
836            public String removeLocalization(
837                    String xml, String key, String requestedLanguageId, boolean cdata,
838                    boolean localized) {
839    
840                    if (Validator.isNull(xml)) {
841                            return StringPool.BLANK;
842                    }
843    
844                    if (!Validator.isXml(xml)) {
845                            return xml;
846                    }
847    
848                    xml = _sanitizeXML(xml);
849    
850                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
851                            LocaleUtil.getSiteDefault());
852    
853                    XMLStreamReader xmlStreamReader = null;
854                    XMLStreamWriter xmlStreamWriter = null;
855    
856                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
857    
858                    ClassLoader contextClassLoader =
859                            ClassLoaderUtil.getContextClassLoader();
860    
861                    try {
862                            if (contextClassLoader != portalClassLoader) {
863                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
864                            }
865    
866                            XMLInputFactory xmlInputFactory =
867                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
868    
869                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
870                                    new UnsyncStringReader(xml));
871    
872                            String availableLocales = StringPool.BLANK;
873                            String defaultLanguageId = StringPool.BLANK;
874    
875                            // Read root node
876    
877                            if (xmlStreamReader.hasNext()) {
878                                    xmlStreamReader.nextTag();
879    
880                                    availableLocales = xmlStreamReader.getAttributeValue(
881                                            null, _AVAILABLE_LOCALES);
882                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
883                                            null, _DEFAULT_LOCALE);
884    
885                                    if (Validator.isNull(defaultLanguageId)) {
886                                            defaultLanguageId = systemDefaultLanguageId;
887                                    }
888                            }
889    
890                            if ((availableLocales != null) &&
891                                    availableLocales.contains(requestedLanguageId)) {
892    
893                                    availableLocales = StringUtil.removeFromList(
894                                            availableLocales, requestedLanguageId);
895    
896                                    UnsyncStringWriter unsyncStringWriter =
897                                            new UnsyncStringWriter();
898    
899                                    XMLOutputFactory xmlOutputFactory =
900                                            XMLOutputFactory.newInstance();
901    
902                                    xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
903                                            unsyncStringWriter);
904    
905                                    xmlStreamWriter.writeStartDocument();
906                                    xmlStreamWriter.writeStartElement(_ROOT);
907    
908                                    if (localized) {
909                                            xmlStreamWriter.writeAttribute(
910                                                    _AVAILABLE_LOCALES, availableLocales);
911                                            xmlStreamWriter.writeAttribute(
912                                                    _DEFAULT_LOCALE, defaultLanguageId);
913                                    }
914    
915                                    _copyNonExempt(
916                                            xmlStreamReader, xmlStreamWriter, requestedLanguageId,
917                                            defaultLanguageId, cdata);
918    
919                                    xmlStreamWriter.writeEndElement();
920                                    xmlStreamWriter.writeEndDocument();
921    
922                                    xmlStreamWriter.close();
923                                    xmlStreamWriter = null;
924    
925                                    xml = unsyncStringWriter.toString();
926                            }
927                    }
928                    catch (Exception e) {
929                            if (_log.isWarnEnabled()) {
930                                    _log.warn(e, e);
931                            }
932                    }
933                    finally {
934                            if (contextClassLoader != portalClassLoader) {
935                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
936                            }
937    
938                            if (xmlStreamReader != null) {
939                                    try {
940                                            xmlStreamReader.close();
941                                    }
942                                    catch (Exception e) {
943                                    }
944                            }
945    
946                            if (xmlStreamWriter != null) {
947                                    try {
948                                            xmlStreamWriter.close();
949                                    }
950                                    catch (Exception e) {
951                                    }
952                            }
953                    }
954    
955                    return xml;
956            }
957    
958            @Override
959            public void setLocalizedPreferencesValues(
960                            PortletRequest portletRequest, PortletPreferences preferences,
961                            String parameter)
962                    throws Exception {
963    
964                    Map<Locale, String> map = getLocalizationMap(portletRequest, parameter);
965    
966                    for (Map.Entry<Locale, String> entry : map.entrySet()) {
967                            String languageId = LocaleUtil.toLanguageId(entry.getKey());
968                            String value = entry.getValue();
969    
970                            setPreferencesValue(preferences, parameter, languageId, value);
971                    }
972            }
973    
974            @Override
975            public void setPreferencesValue(
976                            PortletPreferences preferences, String key, String languageId,
977                            String value)
978                    throws Exception {
979    
980                    preferences.setValue(getLocalizedName(key, languageId), value);
981            }
982    
983            @Override
984            public void setPreferencesValues(
985                            PortletPreferences preferences, String key, String languageId,
986                            String[] values)
987                    throws Exception {
988    
989                    preferences.setValues(getLocalizedName(key, languageId), values);
990            }
991    
992            @Override
993            public String updateLocalization(
994                    Map<Locale, String> localizationMap, String xml, String key,
995                    String defaultLanguageId) {
996    
997                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
998                            String value = localizationMap.get(locale);
999    
1000                            String languageId = LocaleUtil.toLanguageId(locale);
1001    
1002                            if (Validator.isNotNull(value)) {
1003                                    xml = updateLocalization(
1004                                            xml, key, value, languageId, defaultLanguageId);
1005                            }
1006                            else {
1007                                    xml = removeLocalization(xml, key, languageId);
1008                            }
1009                    }
1010    
1011                    return xml;
1012            }
1013    
1014            @Override
1015            public String updateLocalization(String xml, String key, String value) {
1016                    String defaultLanguageId = LocaleUtil.toLanguageId(
1017                            LocaleUtil.getSiteDefault());
1018    
1019                    return updateLocalization(
1020                            xml, key, value, defaultLanguageId, defaultLanguageId);
1021            }
1022    
1023            @Override
1024            public String updateLocalization(
1025                    String xml, String key, String value, String requestedLanguageId) {
1026    
1027                    String defaultLanguageId = LocaleUtil.toLanguageId(
1028                            LocaleUtil.getSiteDefault());
1029    
1030                    return updateLocalization(
1031                            xml, key, value, requestedLanguageId, defaultLanguageId);
1032            }
1033    
1034            @Override
1035            public String updateLocalization(
1036                    String xml, String key, String value, String requestedLanguageId,
1037                    String defaultLanguageId) {
1038    
1039                    return updateLocalization(
1040                            xml, key, value, requestedLanguageId, defaultLanguageId, false);
1041            }
1042    
1043            @Override
1044            public String updateLocalization(
1045                    String xml, String key, String value, String requestedLanguageId,
1046                    String defaultLanguageId, boolean cdata) {
1047    
1048                    return updateLocalization(
1049                            xml, key, value, requestedLanguageId, defaultLanguageId, cdata,
1050                            true);
1051            }
1052    
1053            @Override
1054            public String updateLocalization(
1055                    String xml, String key, String value, String requestedLanguageId,
1056                    String defaultLanguageId, boolean cdata, boolean localized) {
1057    
1058                    xml = _sanitizeXML(xml);
1059    
1060                    XMLStreamReader xmlStreamReader = null;
1061                    XMLStreamWriter xmlStreamWriter = null;
1062    
1063                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
1064    
1065                    ClassLoader contextClassLoader =
1066                            ClassLoaderUtil.getContextClassLoader();
1067    
1068                    try {
1069                            if (contextClassLoader != portalClassLoader) {
1070                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
1071                            }
1072    
1073                            XMLInputFactory xmlInputFactory =
1074                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
1075    
1076                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
1077                                    new UnsyncStringReader(xml));
1078    
1079                            String availableLocales = StringPool.BLANK;
1080    
1081                            // Read root node
1082    
1083                            if (xmlStreamReader.hasNext()) {
1084                                    xmlStreamReader.nextTag();
1085    
1086                                    availableLocales = xmlStreamReader.getAttributeValue(
1087                                            null, _AVAILABLE_LOCALES);
1088    
1089                                    if (Validator.isNull(availableLocales)) {
1090                                            availableLocales = defaultLanguageId;
1091                                    }
1092    
1093                                    if (!availableLocales.contains(requestedLanguageId)) {
1094                                            availableLocales = StringUtil.add(
1095                                                    availableLocales, requestedLanguageId,
1096                                                    StringPool.COMMA);
1097                                    }
1098                            }
1099    
1100                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
1101    
1102                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
1103    
1104                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
1105                                    unsyncStringWriter);
1106    
1107                            xmlStreamWriter.writeStartDocument();
1108                            xmlStreamWriter.writeStartElement(_ROOT);
1109    
1110                            if (localized) {
1111                                    xmlStreamWriter.writeAttribute(
1112                                            _AVAILABLE_LOCALES, availableLocales);
1113                                    xmlStreamWriter.writeAttribute(
1114                                            _DEFAULT_LOCALE, defaultLanguageId);
1115                            }
1116    
1117                            _copyNonExempt(
1118                                    xmlStreamReader, xmlStreamWriter, requestedLanguageId,
1119                                    defaultLanguageId, cdata);
1120    
1121                            xmlStreamWriter.writeStartElement(key);
1122    
1123                            if (localized) {
1124                                    xmlStreamWriter.writeAttribute(
1125                                            _LANGUAGE_ID, requestedLanguageId);
1126                            }
1127    
1128                            if (cdata) {
1129                                    xmlStreamWriter.writeCData(value);
1130                            }
1131                            else {
1132                                    xmlStreamWriter.writeCharacters(value);
1133                            }
1134    
1135                            xmlStreamWriter.writeEndElement();
1136                            xmlStreamWriter.writeEndElement();
1137                            xmlStreamWriter.writeEndDocument();
1138    
1139                            xmlStreamWriter.close();
1140                            xmlStreamWriter = null;
1141    
1142                            xml = unsyncStringWriter.toString();
1143                    }
1144                    catch (Exception e) {
1145                            if (_log.isWarnEnabled()) {
1146                                    _log.warn(e, e);
1147                            }
1148                    }
1149                    finally {
1150                            if (contextClassLoader != portalClassLoader) {
1151                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
1152                            }
1153    
1154                            if (xmlStreamReader != null) {
1155                                    try {
1156                                            xmlStreamReader.close();
1157                                    }
1158                                    catch (Exception e) {
1159                                    }
1160                            }
1161    
1162                            if (xmlStreamWriter != null) {
1163                                    try {
1164                                            xmlStreamWriter.close();
1165                                    }
1166                                    catch (Exception e) {
1167                                    }
1168                            }
1169                    }
1170    
1171                    return xml;
1172            }
1173    
1174            private void _close(XMLStreamWriter xmlStreamWriter) {
1175                    if (xmlStreamWriter != null) {
1176                            try {
1177                                    xmlStreamWriter.close();
1178                            }
1179                            catch (XMLStreamException xmlse) {
1180                            }
1181                    }
1182            }
1183    
1184            private void _copyNonExempt(
1185                            XMLStreamReader xmlStreamReader, XMLStreamWriter xmlStreamWriter,
1186                            String exemptLanguageId, String defaultLanguageId, boolean cdata)
1187                    throws XMLStreamException {
1188    
1189                    while (xmlStreamReader.hasNext()) {
1190                            int event = xmlStreamReader.next();
1191    
1192                            if (event == XMLStreamConstants.START_ELEMENT) {
1193                                    String languageId = xmlStreamReader.getAttributeValue(
1194                                            null, _LANGUAGE_ID);
1195    
1196                                    if (Validator.isNull(languageId)) {
1197                                            languageId = defaultLanguageId;
1198                                    }
1199    
1200                                    if (!languageId.equals(exemptLanguageId)) {
1201                                            xmlStreamWriter.writeStartElement(
1202                                                    xmlStreamReader.getLocalName());
1203                                            xmlStreamWriter.writeAttribute(_LANGUAGE_ID, languageId);
1204    
1205                                            String text = xmlStreamReader.getElementText();
1206    
1207                                            if (cdata) {
1208                                                    xmlStreamWriter.writeCData(text);
1209                                            }
1210                                            else {
1211                                                    xmlStreamWriter.writeCharacters(text);
1212                                            }
1213    
1214                                            xmlStreamWriter.writeEndElement();
1215                                    }
1216                            }
1217                            else if (event == XMLStreamConstants.END_DOCUMENT) {
1218                                    break;
1219                            }
1220                    }
1221            }
1222    
1223            private String _getCachedValue(
1224                    String xml, String requestedLanguageId, boolean useDefault) {
1225    
1226                    String value = null;
1227    
1228                    Map<Tuple, String> valueMap = _cache.get(xml);
1229    
1230                    if (valueMap != null) {
1231                            Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1232    
1233                            value = valueMap.get(subkey);
1234                    }
1235    
1236                    return value;
1237            }
1238    
1239            private String _getDefaultLocalizedName(String name) {
1240                    String defaultLanguageId = LocaleUtil.toLanguageId(
1241                            LocaleUtil.getSiteDefault());
1242    
1243                    return getLocalizedName(name, defaultLanguageId);
1244            }
1245    
1246            private String _getLocalization(
1247                    String bundleName, Locale locale, ClassLoader classLoader, String key,
1248                    String defaultValue) {
1249    
1250                    ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(
1251                            bundleName, locale, classLoader);
1252    
1253                    String value = null;
1254    
1255                    if (resourceBundle.containsKey(key)) {
1256                            try {
1257                                    value = ResourceBundleUtil.getString(resourceBundle, key);
1258    
1259                                    value = new String(
1260                                            value.getBytes(StringPool.ISO_8859_1), StringPool.UTF8);
1261                            }
1262                            catch (Exception e) {
1263                            }
1264                    }
1265    
1266                    if (Validator.isNotNull(value)) {
1267                            value = LanguageResources.fixValue(value);
1268                    }
1269                    else {
1270                            value = LanguageUtil.get(locale, key, defaultValue);
1271                    }
1272    
1273                    return value;
1274            }
1275    
1276            private String _getRootAttributeValue(
1277                    Document document, String name, String defaultValue) {
1278    
1279                    Element rootElement = document.getRootElement();
1280    
1281                    return rootElement.attributeValue(name, defaultValue);
1282            }
1283    
1284            private String _getRootAttributeValue(
1285                    String xml, String name, String defaultValue) {
1286    
1287                    String value = null;
1288    
1289                    XMLStreamReader xmlStreamReader = null;
1290    
1291                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
1292    
1293                    ClassLoader contextClassLoader =
1294                            ClassLoaderUtil.getContextClassLoader();
1295    
1296                    try {
1297                            if (contextClassLoader != portalClassLoader) {
1298                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
1299                            }
1300    
1301                            XMLInputFactory xmlInputFactory =
1302                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
1303    
1304                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
1305                                    new UnsyncStringReader(xml));
1306    
1307                            if (xmlStreamReader.hasNext()) {
1308                                    xmlStreamReader.nextTag();
1309    
1310                                    value = xmlStreamReader.getAttributeValue(null, name);
1311                            }
1312                    }
1313                    catch (Exception e) {
1314                            if (_log.isWarnEnabled()) {
1315                                    _log.warn(e, e);
1316                            }
1317                    }
1318                    finally {
1319                            if (contextClassLoader != portalClassLoader) {
1320                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
1321                            }
1322    
1323                            if (xmlStreamReader != null) {
1324                                    try {
1325                                            xmlStreamReader.close();
1326                                    }
1327                                    catch (Exception e) {
1328                                    }
1329                            }
1330                    }
1331    
1332                    if (Validator.isNull(value)) {
1333                            value = defaultValue;
1334                    }
1335    
1336                    return value;
1337            }
1338    
1339            private String _sanitizeXML(String xml) {
1340                    if (Validator.isNull(xml) || !xml.contains("<root")) {
1341                            xml = _EMPTY_ROOT_NODE;
1342                    }
1343    
1344                    return xml;
1345            }
1346    
1347            private void _setCachedValue(
1348                    String xml, String requestedLanguageId, boolean useDefault,
1349                    String value) {
1350    
1351                    if (Validator.isNotNull(xml) && !xml.equals(_EMPTY_ROOT_NODE)) {
1352                            synchronized (_cache) {
1353                                    Map<Tuple, String> map = _cache.get(xml);
1354    
1355                                    if (map == null) {
1356                                            map = new HashMap<>();
1357                                    }
1358    
1359                                    Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1360    
1361                                    map.put(subkey, value);
1362    
1363                                    _cache.put(xml, map);
1364                            }
1365                    }
1366            }
1367    
1368            private static final String _AVAILABLE_LOCALES = "available-locales";
1369    
1370            private static final String _DEFAULT_LOCALE = "default-locale";
1371    
1372            private static final String _EMPTY_ROOT_NODE = "<root />";
1373    
1374            private static final String _LANGUAGE_ID = "language-id";
1375    
1376            private static final String _ROOT = "root";
1377    
1378            private static final Log _log = LogFactoryUtil.getLog(
1379                    LocalizationImpl.class);
1380    
1381            private final Map<String, Map<Tuple, String>> _cache = new ReferenceMap(
1382                    ReferenceMap.SOFT, ReferenceMap.HARD);
1383    
1384    }