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