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.security.xml.SecureXMLFactoryProviderUtil;
025    import com.liferay.portal.kernel.settings.LocalizedValuesMap;
026    import com.liferay.portal.kernel.settings.Settings;
027    import com.liferay.portal.kernel.util.ArrayUtil;
028    import com.liferay.portal.kernel.util.ClassLoaderUtil;
029    import com.liferay.portal.kernel.util.LocaleUtil;
030    import com.liferay.portal.kernel.util.Localization;
031    import com.liferay.portal.kernel.util.ParamUtil;
032    import com.liferay.portal.kernel.util.PrefsParamUtil;
033    import com.liferay.portal.kernel.util.ResourceBundleUtil;
034    import com.liferay.portal.kernel.util.StringBundler;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.StringUtil;
037    import com.liferay.portal.kernel.util.Tuple;
038    import com.liferay.portal.kernel.util.Validator;
039    import com.liferay.portal.kernel.xml.Document;
040    import com.liferay.portal.kernel.xml.Element;
041    import com.liferay.portal.language.LanguageResources;
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                    Class<?> clazz = getClass();
401    
402                    return getLocalizationMap(
403                            preferences, preferenceName, propertyName, defaultPropertyValue,
404                            clazz.getClassLoader());
405            }
406    
407            @Override
408            public Map<Locale, String> getLocalizationMap(
409                    PortletPreferences preferences, String preferenceName,
410                    String propertyName, String defaultPropertyValue,
411                    ClassLoader classLoader) {
412    
413                    Map<Locale, String> map = new HashMap<>();
414    
415                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
416                            String localizedPreference = getLocalizedName(
417                                    preferenceName, LocaleUtil.toLanguageId(locale));
418    
419                            map.put(
420                                    locale,
421                                    preferences.getValue(localizedPreference, StringPool.BLANK));
422                    }
423    
424                    if (Validator.isNull(propertyName)) {
425                            return map;
426                    }
427    
428                    Locale defaultLocale = LocaleUtil.getSiteDefault();
429    
430                    String defaultValue = map.get(defaultLocale);
431    
432                    if (Validator.isNotNull(defaultValue)) {
433                            return map;
434                    }
435    
436                    map.put(
437                            defaultLocale, ContentUtil.get(classLoader, defaultPropertyValue));
438    
439                    return map;
440            }
441    
442            @Override
443            public Map<Locale, String> getLocalizationMap(
444                    PortletRequest portletRequest, String parameter) {
445    
446                    return getLocalizationMap(
447                            portletRequest, parameter, new HashMap<Locale, String>());
448            }
449    
450            @Override
451            public Map<Locale, String> getLocalizationMap(
452                    PortletRequest portletRequest, String parameter,
453                    Map<Locale, String> defaultValues) {
454    
455                    Map<Locale, String> map = new HashMap<>();
456    
457                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
458                            String localizedParameter = getLocalizedName(
459                                    parameter, LocaleUtil.toLanguageId(locale));
460    
461                            map.put(
462                                    locale,
463                                    ParamUtil.getString(
464                                            portletRequest, localizedParameter,
465                                            defaultValues.get(locale)));
466                    }
467    
468                    return map;
469            }
470    
471            @Override
472            public Map<Locale, String> getLocalizationMap(String xml) {
473                    return getLocalizationMap(xml, false);
474            }
475    
476            @Override
477            public Map<Locale, String> getLocalizationMap(
478                    String xml, boolean useDefault) {
479    
480                    Map<Locale, String> map = new HashMap<>();
481    
482                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
483                            String languageId = LocaleUtil.toLanguageId(locale);
484    
485                            String value = getLocalization(xml, languageId, useDefault);
486    
487                            if (Validator.isNotNull(value)) {
488                                    map.put(locale, value);
489                            }
490                    }
491    
492                    return map;
493            }
494    
495            @Override
496            public Map<Locale, String> getLocalizationMap(
497                    String bundleName, ClassLoader classLoader, String key,
498                    boolean includeBetaLocales) {
499    
500                    if (key == null) {
501                            return null;
502                    }
503    
504                    Map<Locale, String> map = new HashMap<>();
505    
506                    Locale defaultLocale = LocaleUtil.getSiteDefault();
507    
508                    String defaultValue = _getLocalization(
509                            bundleName, defaultLocale, classLoader, key, key);
510    
511                    map.put(defaultLocale, defaultValue);
512    
513                    Set<Locale> locales = null;
514    
515                    if (includeBetaLocales) {
516                            locales = LanguageUtil.getAvailableLocales();
517                    }
518                    else {
519                            locales = LanguageUtil.getSupportedLocales();
520                    }
521    
522                    for (Locale locale : locales) {
523                            if (locale.equals(defaultLocale)) {
524                                    continue;
525                            }
526    
527                            String value = _getLocalization(
528                                    bundleName, locale, classLoader, key, null);
529    
530                            if (Validator.isNotNull(value) && !value.equals(defaultValue)) {
531                                    map.put(locale, value);
532                            }
533                    }
534    
535                    return map;
536            }
537    
538            @Override
539            public Map<Locale, String> getLocalizationMap(
540                    String[] languageIds, String[] values) {
541    
542                    Map<Locale, String> map = new HashMap<>();
543    
544                    for (int i = 0; i < values.length; i++) {
545                            Locale locale = LocaleUtil.fromLanguageId(languageIds[i]);
546    
547                            map.put(locale, values[i]);
548                    }
549    
550                    return map;
551            }
552    
553            @Override
554            public String getLocalizationXmlFromPreferences(
555                    PortletPreferences preferences, PortletRequest portletRequest,
556                    String parameter) {
557    
558                    return getLocalizationXmlFromPreferences(
559                            preferences, portletRequest, parameter, null, null);
560            }
561    
562            @Override
563            public String getLocalizationXmlFromPreferences(
564                    PortletPreferences preferences, PortletRequest portletRequest,
565                    String parameter, String defaultValue) {
566    
567                    return getLocalizationXmlFromPreferences(
568                            preferences, portletRequest, parameter, defaultValue, null);
569            }
570    
571            @Override
572            public String getLocalizationXmlFromPreferences(
573                    PortletPreferences preferences, PortletRequest portletRequest,
574                    String parameter, String prefix, String defaultValue) {
575    
576                    String xml = null;
577    
578                    Locale defaultLocale = LocaleUtil.getSiteDefault();
579                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
580    
581                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
582                            String languageId = LocaleUtil.toLanguageId(locale);
583    
584                            String localizedKey = getLocalizedName(parameter, languageId);
585    
586                            String prefixedLocalizedKey = localizedKey;
587    
588                            if (Validator.isNotNull(prefix)) {
589                                    prefixedLocalizedKey = prefix + "--" + localizedKey + "--";
590                            }
591    
592                            String value = ParamUtil.getString(
593                                    portletRequest, prefixedLocalizedKey,
594                                    preferences.getValue(localizedKey, null));
595    
596                            if (value != null) {
597                                    xml = updateLocalization(xml, parameter, value, languageId);
598                            }
599                    }
600    
601                    if (getLocalization(xml, defaultLanguageId, true, null) == null) {
602                            String oldValue = PrefsParamUtil.getString(
603                                    preferences, portletRequest, parameter, defaultValue);
604    
605                            if (Validator.isNotNull(oldValue)) {
606                                    xml = updateLocalization(xml, parameter, oldValue);
607                            }
608                    }
609    
610                    return xml;
611            }
612    
613            @Override
614            public String getLocalizedName(String name, String languageId) {
615                    return name.concat(StringPool.UNDERLINE).concat(languageId);
616            }
617    
618            @Override
619            public Map<Locale, String> getMap(LocalizedValuesMap localizedValuesMap) {
620                    Map<Locale, String> map = localizedValuesMap.getValues();
621    
622                    Locale locale = LocaleUtil.getDefault();
623    
624                    if (map.get(locale) == null) {
625                            map.put(locale, localizedValuesMap.getDefaultValue());
626                    }
627    
628                    return map;
629            }
630    
631            /**
632             * @deprecated As of 7.0.0
633             */
634            @Deprecated
635            @Override
636            public String getPreferencesKey(String key, String languageId) {
637                    String defaultLanguageId = LocaleUtil.toLanguageId(
638                            LocaleUtil.getSiteDefault());
639    
640                    if (!languageId.equals(defaultLanguageId)) {
641                            key = getLocalizedName(key, languageId);
642                    }
643    
644                    return key;
645            }
646    
647            @Override
648            public String getPreferencesValue(
649                    PortletPreferences preferences, String key, String languageId) {
650    
651                    return getPreferencesValue(preferences, key, languageId, true);
652            }
653    
654            @Override
655            public String getPreferencesValue(
656                    PortletPreferences preferences, String key, String languageId,
657                    boolean useDefault) {
658    
659                    String localizedKey = getLocalizedName(key, languageId);
660    
661                    String value = preferences.getValue(localizedKey, StringPool.BLANK);
662    
663                    if (useDefault && Validator.isNull(value)) {
664                            value = preferences.getValue(
665                                    _getDefaultLocalizedName(key), StringPool.BLANK);
666    
667                            if (Validator.isNull(value)) {
668                                    value = preferences.getValue(key, StringPool.BLANK);
669                            }
670                    }
671    
672                    return value;
673            }
674    
675            @Override
676            public String[] getPreferencesValues(
677                    PortletPreferences preferences, String key, String languageId) {
678    
679                    return getPreferencesValues(preferences, key, languageId, true);
680            }
681    
682            @Override
683            public String[] getPreferencesValues(
684                    PortletPreferences preferences, String key, String languageId,
685                    boolean useDefault) {
686    
687                    String localizedKey = getLocalizedName(key, languageId);
688    
689                    String[] values = preferences.getValues(localizedKey, new String[0]);
690    
691                    if (useDefault && ArrayUtil.isEmpty(values)) {
692                            values = preferences.getValues(
693                                    _getDefaultLocalizedName(key), new String[0]);
694    
695                            if (ArrayUtil.isEmpty(values)) {
696                                    values = preferences.getValues(key, new String[0]);
697                            }
698                    }
699    
700                    return values;
701            }
702    
703            @Override
704            public String getSettingsValue(
705                    Settings settings, String key, String languageId) {
706    
707                    return getSettingsValue(settings, key, languageId, true);
708            }
709    
710            @Override
711            public String getSettingsValue(
712                    Settings settings, String key, String languageId, boolean useDefault) {
713    
714                    String localizedKey = getLocalizedName(key, languageId);
715    
716                    String value = settings.getValue(localizedKey, StringPool.BLANK);
717    
718                    if (useDefault && Validator.isNull(value)) {
719                            value = settings.getValue(
720                                    _getDefaultLocalizedName(key), StringPool.BLANK);
721    
722                            if (Validator.isNull(value)) {
723                                    value = settings.getValue(key, StringPool.BLANK);
724                            }
725                    }
726    
727                    return value;
728            }
729    
730            @Override
731            public String[] getSettingsValues(
732                    Settings settings, String key, String languageId) {
733    
734                    return getSettingsValues(settings, key, languageId, true);
735            }
736    
737            @Override
738            public String[] getSettingsValues(
739                    Settings settings, String key, String languageId, boolean useDefault) {
740    
741                    String localizedKey = getLocalizedName(key, languageId);
742    
743                    String[] values = settings.getValues(localizedKey, new String[0]);
744    
745                    if (useDefault && ArrayUtil.isEmpty(values)) {
746                            values = settings.getValues(
747                                    _getDefaultLocalizedName(key), new String[0]);
748    
749                            if (ArrayUtil.isEmpty(values)) {
750                                    values = settings.getValues(key, new String[0]);
751                            }
752                    }
753    
754                    return values;
755            }
756    
757            @Override
758            public String getXml(LocalizedValuesMap localizedValuesMap, String key) {
759                    XMLStreamWriter xmlStreamWriter = null;
760    
761                    try {
762                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
763    
764                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
765    
766                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
767                                    unsyncStringWriter);
768    
769                            xmlStreamWriter.writeStartDocument();
770    
771                            xmlStreamWriter.writeStartElement("root");
772    
773                            Set<Locale> availableLocales = LanguageUtil.getAvailableLocales();
774    
775                            xmlStreamWriter.writeAttribute(
776                                    "available-locales", StringUtil.merge(availableLocales));
777    
778                            Locale defaultLocale = LocaleUtil.getSiteDefault();
779    
780                            xmlStreamWriter.writeAttribute(
781                                    "default-locale", defaultLocale.toString());
782    
783                            for (Locale locale : availableLocales) {
784                                    String value = localizedValuesMap.get(locale);
785    
786                                    if (value != null) {
787                                            xmlStreamWriter.writeStartElement(key);
788    
789                                            xmlStreamWriter.writeAttribute(
790                                                    "language-id", locale.toString());
791    
792                                            xmlStreamWriter.writeCharacters(value);
793    
794                                            xmlStreamWriter.writeEndElement();
795                                    }
796                            }
797    
798                            xmlStreamWriter.writeEndElement();
799    
800                            xmlStreamWriter.writeEndDocument();
801    
802                            return unsyncStringWriter.toString();
803                    }
804                    catch (XMLStreamException xmlse) {
805                            throw new RuntimeException(xmlse);
806                    }
807                    finally {
808                            _close(xmlStreamWriter);
809                    }
810            }
811    
812            @Override
813            public String removeLocalization(
814                    String xml, String key, String requestedLanguageId) {
815    
816                    return removeLocalization(xml, key, requestedLanguageId, false);
817            }
818    
819            @Override
820            public String removeLocalization(
821                    String xml, String key, String requestedLanguageId, boolean cdata) {
822    
823                    return removeLocalization(xml, key, requestedLanguageId, cdata, true);
824            }
825    
826            @Override
827            public String removeLocalization(
828                    String xml, String key, String requestedLanguageId, boolean cdata,
829                    boolean localized) {
830    
831                    if (Validator.isNull(xml)) {
832                            return StringPool.BLANK;
833                    }
834    
835                    if (!Validator.isXml(xml)) {
836                            return xml;
837                    }
838    
839                    xml = _sanitizeXML(xml);
840    
841                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
842                            LocaleUtil.getSiteDefault());
843    
844                    XMLStreamReader xmlStreamReader = null;
845                    XMLStreamWriter xmlStreamWriter = null;
846    
847                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
848    
849                    ClassLoader contextClassLoader =
850                            ClassLoaderUtil.getContextClassLoader();
851    
852                    try {
853                            if (contextClassLoader != portalClassLoader) {
854                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
855                            }
856    
857                            XMLInputFactory xmlInputFactory =
858                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
859    
860                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
861                                    new UnsyncStringReader(xml));
862    
863                            String availableLocales = StringPool.BLANK;
864                            String defaultLanguageId = StringPool.BLANK;
865    
866                            // Read root node
867    
868                            if (xmlStreamReader.hasNext()) {
869                                    xmlStreamReader.nextTag();
870    
871                                    availableLocales = xmlStreamReader.getAttributeValue(
872                                            null, _AVAILABLE_LOCALES);
873                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
874                                            null, _DEFAULT_LOCALE);
875    
876                                    if (Validator.isNull(defaultLanguageId)) {
877                                            defaultLanguageId = systemDefaultLanguageId;
878                                    }
879                            }
880    
881                            if ((availableLocales != null) &&
882                                    availableLocales.contains(requestedLanguageId)) {
883    
884                                    availableLocales = StringUtil.removeFromList(
885                                            availableLocales, requestedLanguageId);
886    
887                                    UnsyncStringWriter unsyncStringWriter =
888                                            new UnsyncStringWriter();
889    
890                                    XMLOutputFactory xmlOutputFactory =
891                                            XMLOutputFactory.newInstance();
892    
893                                    xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
894                                            unsyncStringWriter);
895    
896                                    xmlStreamWriter.writeStartDocument();
897                                    xmlStreamWriter.writeStartElement(_ROOT);
898    
899                                    if (localized) {
900                                            xmlStreamWriter.writeAttribute(
901                                                    _AVAILABLE_LOCALES, availableLocales);
902                                            xmlStreamWriter.writeAttribute(
903                                                    _DEFAULT_LOCALE, defaultLanguageId);
904                                    }
905    
906                                    _copyNonExempt(
907                                            xmlStreamReader, xmlStreamWriter, requestedLanguageId,
908                                            defaultLanguageId, cdata);
909    
910                                    xmlStreamWriter.writeEndElement();
911                                    xmlStreamWriter.writeEndDocument();
912    
913                                    xmlStreamWriter.close();
914                                    xmlStreamWriter = null;
915    
916                                    xml = unsyncStringWriter.toString();
917                            }
918                    }
919                    catch (Exception e) {
920                            if (_log.isWarnEnabled()) {
921                                    _log.warn(e, e);
922                            }
923                    }
924                    finally {
925                            if (contextClassLoader != portalClassLoader) {
926                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
927                            }
928    
929                            if (xmlStreamReader != null) {
930                                    try {
931                                            xmlStreamReader.close();
932                                    }
933                                    catch (Exception e) {
934                                    }
935                            }
936    
937                            if (xmlStreamWriter != null) {
938                                    try {
939                                            xmlStreamWriter.close();
940                                    }
941                                    catch (Exception e) {
942                                    }
943                            }
944                    }
945    
946                    return xml;
947            }
948    
949            @Override
950            public void setLocalizedPreferencesValues(
951                            PortletRequest portletRequest, PortletPreferences preferences,
952                            String parameter)
953                    throws Exception {
954    
955                    Map<Locale, String> map = getLocalizationMap(portletRequest, parameter);
956    
957                    for (Map.Entry<Locale, String> entry : map.entrySet()) {
958                            String languageId = LocaleUtil.toLanguageId(entry.getKey());
959                            String value = entry.getValue();
960    
961                            setPreferencesValue(preferences, parameter, languageId, value);
962                    }
963            }
964    
965            @Override
966            public void setPreferencesValue(
967                            PortletPreferences preferences, String key, String languageId,
968                            String value)
969                    throws Exception {
970    
971                    preferences.setValue(getLocalizedName(key, languageId), value);
972            }
973    
974            @Override
975            public void setPreferencesValues(
976                            PortletPreferences preferences, String key, String languageId,
977                            String[] values)
978                    throws Exception {
979    
980                    preferences.setValues(getLocalizedName(key, languageId), values);
981            }
982    
983            @Override
984            public String updateLocalization(
985                    Map<Locale, String> localizationMap, String xml, String key,
986                    String defaultLanguageId) {
987    
988                    for (Locale locale : LanguageUtil.getAvailableLocales()) {
989                            String value = localizationMap.get(locale);
990    
991                            String languageId = LocaleUtil.toLanguageId(locale);
992    
993                            if (Validator.isNotNull(value)) {
994                                    xml = updateLocalization(
995                                            xml, key, value, languageId, defaultLanguageId);
996                            }
997                            else {
998                                    xml = removeLocalization(xml, key, languageId);
999                            }
1000                    }
1001    
1002                    return xml;
1003            }
1004    
1005            @Override
1006            public String updateLocalization(String xml, String key, String value) {
1007                    String defaultLanguageId = LocaleUtil.toLanguageId(
1008                            LocaleUtil.getSiteDefault());
1009    
1010                    return updateLocalization(
1011                            xml, key, value, defaultLanguageId, defaultLanguageId);
1012            }
1013    
1014            @Override
1015            public String updateLocalization(
1016                    String xml, String key, String value, String requestedLanguageId) {
1017    
1018                    String defaultLanguageId = LocaleUtil.toLanguageId(
1019                            LocaleUtil.getSiteDefault());
1020    
1021                    return updateLocalization(
1022                            xml, key, value, requestedLanguageId, defaultLanguageId);
1023            }
1024    
1025            @Override
1026            public String updateLocalization(
1027                    String xml, String key, String value, String requestedLanguageId,
1028                    String defaultLanguageId) {
1029    
1030                    return updateLocalization(
1031                            xml, key, value, requestedLanguageId, defaultLanguageId, false);
1032            }
1033    
1034            @Override
1035            public String updateLocalization(
1036                    String xml, String key, String value, String requestedLanguageId,
1037                    String defaultLanguageId, boolean cdata) {
1038    
1039                    return updateLocalization(
1040                            xml, key, value, requestedLanguageId, defaultLanguageId, cdata,
1041                            true);
1042            }
1043    
1044            @Override
1045            public String updateLocalization(
1046                    String xml, String key, String value, String requestedLanguageId,
1047                    String defaultLanguageId, boolean cdata, boolean localized) {
1048    
1049                    xml = _sanitizeXML(xml);
1050    
1051                    XMLStreamReader xmlStreamReader = null;
1052                    XMLStreamWriter xmlStreamWriter = null;
1053    
1054                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
1055    
1056                    ClassLoader contextClassLoader =
1057                            ClassLoaderUtil.getContextClassLoader();
1058    
1059                    try {
1060                            if (contextClassLoader != portalClassLoader) {
1061                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
1062                            }
1063    
1064                            XMLInputFactory xmlInputFactory =
1065                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
1066    
1067                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
1068                                    new UnsyncStringReader(xml));
1069    
1070                            String availableLocales = StringPool.BLANK;
1071    
1072                            // Read root node
1073    
1074                            if (xmlStreamReader.hasNext()) {
1075                                    xmlStreamReader.nextTag();
1076    
1077                                    availableLocales = xmlStreamReader.getAttributeValue(
1078                                            null, _AVAILABLE_LOCALES);
1079    
1080                                    if (Validator.isNull(availableLocales)) {
1081                                            availableLocales = defaultLanguageId;
1082                                    }
1083    
1084                                    if (!availableLocales.contains(requestedLanguageId)) {
1085                                            availableLocales = StringUtil.add(
1086                                                    availableLocales, requestedLanguageId,
1087                                                    StringPool.COMMA);
1088                                    }
1089                            }
1090    
1091                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
1092    
1093                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
1094    
1095                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
1096                                    unsyncStringWriter);
1097    
1098                            xmlStreamWriter.writeStartDocument();
1099                            xmlStreamWriter.writeStartElement(_ROOT);
1100    
1101                            if (localized) {
1102                                    xmlStreamWriter.writeAttribute(
1103                                            _AVAILABLE_LOCALES, availableLocales);
1104                                    xmlStreamWriter.writeAttribute(
1105                                            _DEFAULT_LOCALE, defaultLanguageId);
1106                            }
1107    
1108                            _copyNonExempt(
1109                                    xmlStreamReader, xmlStreamWriter, requestedLanguageId,
1110                                    defaultLanguageId, cdata);
1111    
1112                            xmlStreamWriter.writeStartElement(key);
1113    
1114                            if (localized) {
1115                                    xmlStreamWriter.writeAttribute(
1116                                            _LANGUAGE_ID, requestedLanguageId);
1117                            }
1118    
1119                            if (cdata) {
1120                                    xmlStreamWriter.writeCData(value);
1121                            }
1122                            else {
1123                                    xmlStreamWriter.writeCharacters(value);
1124                            }
1125    
1126                            xmlStreamWriter.writeEndElement();
1127                            xmlStreamWriter.writeEndElement();
1128                            xmlStreamWriter.writeEndDocument();
1129    
1130                            xmlStreamWriter.close();
1131                            xmlStreamWriter = null;
1132    
1133                            xml = unsyncStringWriter.toString();
1134                    }
1135                    catch (Exception e) {
1136                            if (_log.isWarnEnabled()) {
1137                                    _log.warn(e, e);
1138                            }
1139                    }
1140                    finally {
1141                            if (contextClassLoader != portalClassLoader) {
1142                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
1143                            }
1144    
1145                            if (xmlStreamReader != null) {
1146                                    try {
1147                                            xmlStreamReader.close();
1148                                    }
1149                                    catch (Exception e) {
1150                                    }
1151                            }
1152    
1153                            if (xmlStreamWriter != null) {
1154                                    try {
1155                                            xmlStreamWriter.close();
1156                                    }
1157                                    catch (Exception e) {
1158                                    }
1159                            }
1160                    }
1161    
1162                    return xml;
1163            }
1164    
1165            private void _close(XMLStreamWriter xmlStreamWriter) {
1166                    if (xmlStreamWriter != null) {
1167                            try {
1168                                    xmlStreamWriter.close();
1169                            }
1170                            catch (XMLStreamException xmlse) {
1171                            }
1172                    }
1173            }
1174    
1175            private void _copyNonExempt(
1176                            XMLStreamReader xmlStreamReader, XMLStreamWriter xmlStreamWriter,
1177                            String exemptLanguageId, String defaultLanguageId, boolean cdata)
1178                    throws XMLStreamException {
1179    
1180                    while (xmlStreamReader.hasNext()) {
1181                            int event = xmlStreamReader.next();
1182    
1183                            if (event == XMLStreamConstants.START_ELEMENT) {
1184                                    String languageId = xmlStreamReader.getAttributeValue(
1185                                            null, _LANGUAGE_ID);
1186    
1187                                    if (Validator.isNull(languageId)) {
1188                                            languageId = defaultLanguageId;
1189                                    }
1190    
1191                                    if (!languageId.equals(exemptLanguageId)) {
1192                                            xmlStreamWriter.writeStartElement(
1193                                                    xmlStreamReader.getLocalName());
1194                                            xmlStreamWriter.writeAttribute(_LANGUAGE_ID, languageId);
1195    
1196                                            String text = xmlStreamReader.getElementText();
1197    
1198                                            if (cdata) {
1199                                                    xmlStreamWriter.writeCData(text);
1200                                            }
1201                                            else {
1202                                                    xmlStreamWriter.writeCharacters(text);
1203                                            }
1204    
1205                                            xmlStreamWriter.writeEndElement();
1206                                    }
1207                            }
1208                            else if (event == XMLStreamConstants.END_DOCUMENT) {
1209                                    break;
1210                            }
1211                    }
1212            }
1213    
1214            private String _getCachedValue(
1215                    String xml, String requestedLanguageId, boolean useDefault) {
1216    
1217                    String value = null;
1218    
1219                    Map<Tuple, String> valueMap = _cache.get(xml);
1220    
1221                    if (valueMap != null) {
1222                            Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1223    
1224                            value = valueMap.get(subkey);
1225                    }
1226    
1227                    return value;
1228            }
1229    
1230            private String _getDefaultLocalizedName(String name) {
1231                    String defaultLanguageId = LocaleUtil.toLanguageId(
1232                            LocaleUtil.getSiteDefault());
1233    
1234                    return getLocalizedName(name, defaultLanguageId);
1235            }
1236    
1237            private String _getLocalization(
1238                    String bundleName, Locale locale, ClassLoader classLoader, String key,
1239                    String defaultValue) {
1240    
1241                    ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(
1242                            bundleName, locale, classLoader);
1243    
1244                    String value = null;
1245    
1246                    if (resourceBundle.containsKey(key)) {
1247                            try {
1248                                    value = ResourceBundleUtil.getString(resourceBundle, key);
1249    
1250                                    value = new String(
1251                                            value.getBytes(StringPool.ISO_8859_1), StringPool.UTF8);
1252                            }
1253                            catch (Exception e) {
1254                            }
1255                    }
1256    
1257                    if (Validator.isNotNull(value)) {
1258                            value = LanguageResources.fixValue(value);
1259                    }
1260                    else {
1261                            value = LanguageUtil.get(locale, key, defaultValue);
1262                    }
1263    
1264                    return value;
1265            }
1266    
1267            private String _getRootAttributeValue(
1268                    Document document, String name, String defaultValue) {
1269    
1270                    Element rootElement = document.getRootElement();
1271    
1272                    return rootElement.attributeValue(name, defaultValue);
1273            }
1274    
1275            private String _getRootAttributeValue(
1276                    String xml, String name, String defaultValue) {
1277    
1278                    String value = null;
1279    
1280                    XMLStreamReader xmlStreamReader = null;
1281    
1282                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
1283    
1284                    ClassLoader contextClassLoader =
1285                            ClassLoaderUtil.getContextClassLoader();
1286    
1287                    try {
1288                            if (contextClassLoader != portalClassLoader) {
1289                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
1290                            }
1291    
1292                            XMLInputFactory xmlInputFactory =
1293                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
1294    
1295                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
1296                                    new UnsyncStringReader(xml));
1297    
1298                            if (xmlStreamReader.hasNext()) {
1299                                    xmlStreamReader.nextTag();
1300    
1301                                    value = xmlStreamReader.getAttributeValue(null, name);
1302                            }
1303                    }
1304                    catch (Exception e) {
1305                            if (_log.isWarnEnabled()) {
1306                                    _log.warn(e, e);
1307                            }
1308                    }
1309                    finally {
1310                            if (contextClassLoader != portalClassLoader) {
1311                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
1312                            }
1313    
1314                            if (xmlStreamReader != null) {
1315                                    try {
1316                                            xmlStreamReader.close();
1317                                    }
1318                                    catch (Exception e) {
1319                                    }
1320                            }
1321                    }
1322    
1323                    if (Validator.isNull(value)) {
1324                            value = defaultValue;
1325                    }
1326    
1327                    return value;
1328            }
1329    
1330            private String _sanitizeXML(String xml) {
1331                    if (Validator.isNull(xml) || !xml.contains("<root")) {
1332                            xml = _EMPTY_ROOT_NODE;
1333                    }
1334    
1335                    return xml;
1336            }
1337    
1338            private void _setCachedValue(
1339                    String xml, String requestedLanguageId, boolean useDefault,
1340                    String value) {
1341    
1342                    if (Validator.isNotNull(xml) && !xml.equals(_EMPTY_ROOT_NODE)) {
1343                            synchronized (_cache) {
1344                                    Map<Tuple, String> map = _cache.get(xml);
1345    
1346                                    if (map == null) {
1347                                            map = new HashMap<>();
1348                                    }
1349    
1350                                    Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1351    
1352                                    map.put(subkey, value);
1353    
1354                                    _cache.put(xml, map);
1355                            }
1356                    }
1357            }
1358    
1359            private static final String _AVAILABLE_LOCALES = "available-locales";
1360    
1361            private static final String _DEFAULT_LOCALE = "default-locale";
1362    
1363            private static final String _EMPTY_ROOT_NODE = "<root />";
1364    
1365            private static final String _LANGUAGE_ID = "language-id";
1366    
1367            private static final String _ROOT = "root";
1368    
1369            private static final Log _log = LogFactoryUtil.getLog(
1370                    LocalizationImpl.class);
1371    
1372            private final Map<String, Map<Tuple, String>> _cache = new ReferenceMap(
1373                    ReferenceMap.SOFT, ReferenceMap.HARD);
1374    
1375    }