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