001    /**
002     * Copyright (c) 2000-2013 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.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.LocaleUtil;
026    import com.liferay.portal.kernel.util.Localization;
027    import com.liferay.portal.kernel.util.ParamUtil;
028    import com.liferay.portal.kernel.util.PrefsParamUtil;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Tuple;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.language.LanguageResources;
035    
036    import java.util.HashMap;
037    import java.util.Locale;
038    import java.util.Map;
039    import java.util.ResourceBundle;
040    
041    import javax.portlet.PortletPreferences;
042    import javax.portlet.PortletRequest;
043    
044    import javax.xml.stream.XMLInputFactory;
045    import javax.xml.stream.XMLOutputFactory;
046    import javax.xml.stream.XMLStreamConstants;
047    import javax.xml.stream.XMLStreamException;
048    import javax.xml.stream.XMLStreamReader;
049    import javax.xml.stream.XMLStreamWriter;
050    
051    import org.apache.commons.collections.map.ReferenceMap;
052    
053    /**
054     * @author Alexander Chow
055     * @author Jorge Ferrer
056     * @author Mauro Mariuzzo
057     * @author Julio Camarero
058     * @author Brian Wing Shun Chan
059     * @author Connor McKay
060     */
061    @DoPrivileged
062    public class LocalizationImpl implements Localization {
063    
064            @Override
065            public Object deserialize(JSONObject jsonObject) {
066                    Locale[] locales = LanguageUtil.getAvailableLocales();
067    
068                    Map<Locale, String> map = new HashMap<Locale, String>();
069    
070                    for (Locale locale : locales) {
071                            String languageId = LocaleUtil.toLanguageId(locale);
072    
073                            String value = jsonObject.getString(languageId);
074    
075                            if (Validator.isNotNull(value)) {
076                                    map.put(locale, value);
077                            }
078                    }
079    
080                    return map;
081            }
082    
083            @Override
084            public String[] getAvailableLocales(String xml) {
085                    String attributeValue = _getRootAttribute(
086                            xml, _AVAILABLE_LOCALES, StringPool.BLANK);
087    
088                    return StringUtil.split(attributeValue);
089            }
090    
091            @Override
092            public Locale getDefaultImportLocale(
093                    String className, long classPK, Locale contentDefaultLocale,
094                    Locale[] contentAvailableLocales) {
095    
096                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
097    
098                    if (ArrayUtil.contains(availableLocales, contentDefaultLocale)) {
099                            return contentDefaultLocale;
100                    }
101    
102                    Locale defaultLocale = LocaleUtil.getDefault();
103    
104                    if (ArrayUtil.contains(contentAvailableLocales, defaultLocale)) {
105                            return defaultLocale;
106                    }
107    
108                    for (Locale contentAvailableLocale : contentAvailableLocales) {
109                            if (ArrayUtil.contains(availableLocales, contentAvailableLocale)) {
110                                    return contentAvailableLocale;
111                            }
112                    }
113    
114                    if (_log.isWarnEnabled()) {
115                            StringBundler sb = new StringBundler(9);
116    
117                            sb.append("Language ");
118                            sb.append(LocaleUtil.toLanguageId(contentDefaultLocale));
119                            sb.append(" is missing for ");
120                            sb.append(className);
121                            sb.append(" with primary key ");
122                            sb.append(classPK);
123                            sb.append(". Setting default language to ");
124                            sb.append(LocaleUtil.toLanguageId(defaultLocale));
125                            sb.append(".");
126    
127                            _log.warn(sb.toString());
128                    }
129    
130                    return defaultLocale;
131            }
132    
133            @Override
134            public String getDefaultLocale(String xml) {
135                    String defaultLanguageId = LocaleUtil.toLanguageId(
136                            LocaleUtil.getDefault());
137    
138                    return _getRootAttribute(xml, _DEFAULT_LOCALE, defaultLanguageId);
139            }
140    
141            @Override
142            public String getLocalization(String xml, String requestedLanguageId) {
143                    return getLocalization(xml, requestedLanguageId, true);
144            }
145    
146            @Override
147            public String getLocalization(
148                    String xml, String requestedLanguageId, boolean useDefault) {
149    
150                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
151                            LocaleUtil.getDefault());
152    
153                    if (!Validator.isXml(xml)) {
154                            if (useDefault ||
155                                    requestedLanguageId.equals(systemDefaultLanguageId)) {
156    
157                                    return xml;
158                            }
159                            else {
160                                    return StringPool.BLANK;
161                            }
162                    }
163    
164                    String value = _getCachedValue(xml, requestedLanguageId, useDefault);
165    
166                    if (value != null) {
167                            return value;
168                    }
169                    else {
170                            value = StringPool.BLANK;
171                    }
172    
173                    String priorityLanguageId = null;
174    
175                    Locale requestedLocale = LocaleUtil.fromLanguageId(requestedLanguageId);
176    
177                    if (useDefault &&
178                            LanguageUtil.isDuplicateLanguageCode(
179                                    requestedLocale.getLanguage())) {
180    
181                            Locale priorityLocale = LanguageUtil.getLocale(
182                                    requestedLocale.getLanguage());
183    
184                            if (!requestedLanguageId.equals(priorityLanguageId)) {
185                                    priorityLanguageId = LocaleUtil.toLanguageId(priorityLocale);
186                            }
187                    }
188    
189                    XMLStreamReader xmlStreamReader = null;
190    
191                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
192    
193                    ClassLoader contextClassLoader =
194                            ClassLoaderUtil.getContextClassLoader();
195    
196                    try {
197                            if (contextClassLoader != portalClassLoader) {
198                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
199                            }
200    
201                            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
202    
203                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
204                                    new UnsyncStringReader(xml));
205    
206                            String defaultLanguageId = StringPool.BLANK;
207    
208                            // Skip root node
209    
210                            if (xmlStreamReader.hasNext()) {
211                                    xmlStreamReader.nextTag();
212    
213                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
214                                            null, _DEFAULT_LOCALE);
215    
216                                    if (Validator.isNull(defaultLanguageId)) {
217                                            defaultLanguageId = systemDefaultLanguageId;
218                                    }
219                            }
220    
221                            // Find specified language and/or default language
222    
223                            String defaultValue = StringPool.BLANK;
224                            String priorityValue = StringPool.BLANK;
225    
226                            while (xmlStreamReader.hasNext()) {
227                                    int event = xmlStreamReader.next();
228    
229                                    if (event == XMLStreamConstants.START_ELEMENT) {
230                                            String languageId = xmlStreamReader.getAttributeValue(
231                                                    null, _LANGUAGE_ID);
232    
233                                            if (Validator.isNull(languageId)) {
234                                                    languageId = defaultLanguageId;
235                                            }
236    
237                                            if (languageId.equals(defaultLanguageId) ||
238                                                    languageId.equals(priorityLanguageId) ||
239                                                    languageId.equals(requestedLanguageId)) {
240    
241                                                    String text = xmlStreamReader.getElementText();
242    
243                                                    if (languageId.equals(defaultLanguageId)) {
244                                                            defaultValue = text;
245                                                    }
246    
247                                                    if (languageId.equals(priorityLanguageId)) {
248                                                            priorityValue = text;
249                                                    }
250    
251                                                    if (languageId.equals(requestedLanguageId)) {
252                                                            value = text;
253                                                    }
254    
255                                                    if (Validator.isNotNull(value)) {
256                                                            break;
257                                                    }
258                                            }
259                                    }
260                                    else if (event == XMLStreamConstants.END_DOCUMENT) {
261                                            break;
262                                    }
263                            }
264    
265                            if (useDefault && Validator.isNotNull(priorityLanguageId) &&
266                                    Validator.isNull(value) && Validator.isNotNull(priorityValue)) {
267    
268                                    value = priorityValue;
269                            }
270    
271                            if (useDefault && Validator.isNull(value)) {
272                                    value = defaultValue;
273                            }
274                    }
275                    catch (Exception e) {
276                            if (_log.isWarnEnabled()) {
277                                    _log.warn(e, e);
278                            }
279                    }
280                    finally {
281                            if (contextClassLoader != portalClassLoader) {
282                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
283                            }
284    
285                            if (xmlStreamReader != null) {
286                                    try {
287                                            xmlStreamReader.close();
288                                    }
289                                    catch (Exception e) {
290                                    }
291                            }
292                    }
293    
294                    _setCachedValue(xml, requestedLanguageId, useDefault, value);
295    
296                    return value;
297            }
298    
299            @Override
300            public Map<Locale, String> getLocalizationMap(
301                    PortletPreferences preferences, String parameter) {
302    
303                    Locale[] locales = LanguageUtil.getAvailableLocales();
304    
305                    Map<Locale, String> map = new HashMap<Locale, String>();
306    
307                    for (Locale locale : locales) {
308                            String languageId = LocaleUtil.toLanguageId(locale);
309    
310                            String localeParameter = parameter.concat(
311                                    StringPool.UNDERLINE).concat(languageId);
312    
313                            map.put(
314                                    locale,
315                                    preferences.getValue(localeParameter, StringPool.BLANK));
316                    }
317    
318                    return map;
319            }
320    
321            @Override
322            public Map<Locale, String> getLocalizationMap(
323                    PortletRequest portletRequest, String parameter) {
324    
325                    Locale[] locales = LanguageUtil.getAvailableLocales();
326    
327                    Map<Locale, String> map = new HashMap<Locale, String>();
328    
329                    for (Locale locale : locales) {
330                            String languageId = LocaleUtil.toLanguageId(locale);
331    
332                            String localeParameter = parameter.concat(
333                                    StringPool.UNDERLINE).concat(languageId);
334    
335                            map.put(
336                                    locale, ParamUtil.getString(portletRequest, localeParameter));
337                    }
338    
339                    return map;
340            }
341    
342            @Override
343            public Map<Locale, String> getLocalizationMap(String xml) {
344                    Locale[] locales = LanguageUtil.getAvailableLocales();
345    
346                    Map<Locale, String> map = new HashMap<Locale, String>();
347    
348                    for (Locale locale : locales) {
349                            String languageId = LocaleUtil.toLanguageId(locale);
350    
351                            map.put(locale, getLocalization(xml, languageId, false));
352                    }
353    
354                    return map;
355            }
356    
357            @Override
358            public Map<Locale, String> getLocalizationMap(
359                    String bundleName, ClassLoader classLoader, String key,
360                    boolean includeBetaLocales) {
361    
362                    if (key == null) {
363                            return null;
364                    }
365    
366                    Map<Locale, String> map = new HashMap<Locale, String>();
367    
368                    Locale defaultLocale = LocaleUtil.getDefault();
369    
370                    String defaultValue = _getLocalization(
371                            bundleName, defaultLocale, classLoader, key, key);
372    
373                    map.put(defaultLocale, defaultValue);
374    
375                    Locale[] locales = null;
376    
377                    if (includeBetaLocales) {
378                            locales = LanguageUtil.getAvailableLocales();
379                    }
380                    else {
381                            locales = LanguageUtil.getSupportedLocales();
382                    }
383    
384                    for (Locale locale : locales) {
385                            if (locale.equals(defaultLocale)) {
386                                    continue;
387                            }
388    
389                            String value = _getLocalization(
390                                    bundleName, locale, classLoader, key, null);
391    
392                            if (Validator.isNotNull(value) && !value.equals(defaultValue)) {
393                                    map.put(locale, value);
394                            }
395                    }
396    
397                    return map;
398            }
399    
400            @Override
401            public Map<Locale, String> getLocalizationMap(
402                    String[] languageIds, String[] values) {
403    
404                    Map<Locale, String> map = new HashMap<Locale, String>();
405    
406                    for (int i = 0; i < values.length; i++) {
407                            Locale locale = LocaleUtil.fromLanguageId(languageIds[i]);
408    
409                            map.put(locale, values[i]);
410                    }
411    
412                    return map;
413            }
414    
415            @Override
416            public String getLocalizationXmlFromPreferences(
417                    PortletPreferences preferences, PortletRequest portletRequest,
418                    String parameter) {
419    
420                    String xml = StringPool.BLANK;
421    
422                    Locale[] locales = LanguageUtil.getAvailableLocales();
423                    Locale defaultLocale = LocaleUtil.getDefault();
424                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
425    
426                    for (Locale locale : locales) {
427                            String languageId = LocaleUtil.toLanguageId(locale);
428    
429                            String localParameter =
430                                    parameter + StringPool.UNDERLINE + languageId;
431    
432                            String value = PrefsParamUtil.getString(
433                                    preferences, portletRequest, localParameter);
434    
435                            if (Validator.isNotNull(value)) {
436                                    xml = updateLocalization(xml, parameter, value, languageId);
437                            }
438                    }
439    
440                    if (Validator.isNull(getLocalization(xml, defaultLanguageId))) {
441                            String oldValue = PrefsParamUtil.getString(
442                                    preferences, portletRequest, parameter);
443    
444                            if (Validator.isNotNull(oldValue)) {
445                                    xml = updateLocalization(xml, parameter, oldValue);
446                            }
447                    }
448    
449                    return xml;
450            }
451    
452            @Override
453            public Map<Locale, String> getLocalizedParameter(
454                    PortletRequest portletRequest, String parameter) {
455    
456                    return getLocalizationMap(portletRequest, parameter);
457            }
458    
459            @Override
460            public String getPreferencesKey(String key, String languageId) {
461                    String defaultLanguageId = LocaleUtil.toLanguageId(
462                            LocaleUtil.getDefault());
463    
464                    if (!languageId.equals(defaultLanguageId)) {
465                            key += StringPool.UNDERLINE + languageId;
466                    }
467    
468                    return key;
469            }
470    
471            @Override
472            public String getPreferencesValue(
473                    PortletPreferences preferences, String key, String languageId) {
474    
475                    return getPreferencesValue(preferences, key, languageId, true);
476            }
477    
478            @Override
479            public String getPreferencesValue(
480                    PortletPreferences preferences, String key, String languageId,
481                    boolean useDefault) {
482    
483                    String localizedKey = getPreferencesKey(key, languageId);
484    
485                    String value = preferences.getValue(localizedKey, StringPool.BLANK);
486    
487                    if (useDefault && Validator.isNull(value)) {
488                            value = preferences.getValue(key, StringPool.BLANK);
489                    }
490    
491                    return value;
492            }
493    
494            @Override
495            public String[] getPreferencesValues(
496                    PortletPreferences preferences, String key, String languageId) {
497    
498                    return getPreferencesValues(preferences, key, languageId, true);
499            }
500    
501            @Override
502            public String[] getPreferencesValues(
503                    PortletPreferences preferences, String key, String languageId,
504                    boolean useDefault) {
505    
506                    String localizedKey = getPreferencesKey(key, languageId);
507    
508                    String[] values = preferences.getValues(localizedKey, new String[0]);
509    
510                    if (useDefault && Validator.isNull(values)) {
511                            values = preferences.getValues(key, new String[0]);
512                    }
513    
514                    return values;
515            }
516    
517            @Override
518            public String removeLocalization(
519                    String xml, String key, String requestedLanguageId) {
520    
521                    return removeLocalization(xml, key, requestedLanguageId, false);
522            }
523    
524            @Override
525            public String removeLocalization(
526                    String xml, String key, String requestedLanguageId, boolean cdata) {
527    
528                    return removeLocalization(xml, key, requestedLanguageId, cdata, true);
529            }
530    
531            @Override
532            public String removeLocalization(
533                    String xml, String key, String requestedLanguageId, boolean cdata,
534                    boolean localized) {
535    
536                    if (Validator.isNull(xml)) {
537                            return StringPool.BLANK;
538                    }
539    
540                    if (!Validator.isXml(xml)) {
541                            return xml;
542                    }
543    
544                    xml = _sanitizeXML(xml);
545    
546                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
547                            LocaleUtil.getDefault());
548    
549                    XMLStreamReader xmlStreamReader = null;
550                    XMLStreamWriter xmlStreamWriter = null;
551    
552                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
553    
554                    ClassLoader contextClassLoader =
555                            ClassLoaderUtil.getContextClassLoader();
556    
557                    try {
558                            if (contextClassLoader != portalClassLoader) {
559                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
560                            }
561    
562                            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
563    
564                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
565                                    new UnsyncStringReader(xml));
566    
567                            String availableLocales = StringPool.BLANK;
568                            String defaultLanguageId = StringPool.BLANK;
569    
570                            // Read root node
571    
572                            if (xmlStreamReader.hasNext()) {
573                                    xmlStreamReader.nextTag();
574    
575                                    availableLocales = xmlStreamReader.getAttributeValue(
576                                            null, _AVAILABLE_LOCALES);
577                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
578                                            null, _DEFAULT_LOCALE);
579    
580                                    if (Validator.isNull(defaultLanguageId)) {
581                                            defaultLanguageId = systemDefaultLanguageId;
582                                    }
583                            }
584    
585                            if ((availableLocales != null) &&
586                                    availableLocales.contains(requestedLanguageId)) {
587    
588                                    availableLocales = StringUtil.remove(
589                                            availableLocales, requestedLanguageId, StringPool.COMMA);
590    
591                                    UnsyncStringWriter unsyncStringWriter =
592                                            new UnsyncStringWriter();
593    
594                                    XMLOutputFactory xmlOutputFactory =
595                                            XMLOutputFactory.newInstance();
596    
597                                    xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
598                                            unsyncStringWriter);
599    
600                                    xmlStreamWriter.writeStartDocument();
601                                    xmlStreamWriter.writeStartElement(_ROOT);
602    
603                                    if (localized) {
604                                            xmlStreamWriter.writeAttribute(
605                                                    _AVAILABLE_LOCALES, availableLocales);
606                                            xmlStreamWriter.writeAttribute(
607                                                    _DEFAULT_LOCALE, defaultLanguageId);
608                                    }
609    
610                                    _copyNonExempt(
611                                            xmlStreamReader, xmlStreamWriter, requestedLanguageId,
612                                            defaultLanguageId, cdata);
613    
614                                    xmlStreamWriter.writeEndElement();
615                                    xmlStreamWriter.writeEndDocument();
616    
617                                    xmlStreamWriter.close();
618                                    xmlStreamWriter = null;
619    
620                                    xml = unsyncStringWriter.toString();
621                            }
622                    }
623                    catch (Exception e) {
624                            if (_log.isWarnEnabled()) {
625                                    _log.warn(e, e);
626                            }
627                    }
628                    finally {
629                            if (contextClassLoader != portalClassLoader) {
630                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
631                            }
632    
633                            if (xmlStreamReader != null) {
634                                    try {
635                                            xmlStreamReader.close();
636                                    }
637                                    catch (Exception e) {
638                                    }
639                            }
640    
641                            if (xmlStreamWriter != null) {
642                                    try {
643                                            xmlStreamWriter.close();
644                                    }
645                                    catch (Exception e) {
646                                    }
647                            }
648                    }
649    
650                    return xml;
651            }
652    
653            @Override
654            public void setLocalizedPreferencesValues(
655                            PortletRequest portletRequest, PortletPreferences preferences,
656                            String parameter)
657                    throws Exception {
658    
659                    Map<Locale, String> map = getLocalizationMap(portletRequest, parameter);
660    
661                    for (Map.Entry<Locale, String> entry : map.entrySet()) {
662                            String languageId = LocaleUtil.toLanguageId(entry.getKey());
663                            String value = entry.getValue();
664    
665                            setPreferencesValue(preferences, parameter, languageId, value);
666                    }
667            }
668    
669            @Override
670            public void setPreferencesValue(
671                            PortletPreferences preferences, String key, String languageId,
672                            String value)
673                    throws Exception {
674    
675                    preferences.setValue(getPreferencesKey(key, languageId), value);
676            }
677    
678            @Override
679            public void setPreferencesValues(
680                            PortletPreferences preferences, String key, String languageId,
681                            String[] values)
682                    throws Exception {
683    
684                    preferences.setValues(getPreferencesKey(key, languageId), values);
685            }
686    
687            @Override
688            public String updateLocalization(String xml, String key, String value) {
689                    String defaultLanguageId = LocaleUtil.toLanguageId(
690                            LocaleUtil.getDefault());
691    
692                    return updateLocalization(
693                            xml, key, value, defaultLanguageId, defaultLanguageId);
694            }
695    
696            @Override
697            public String updateLocalization(
698                    String xml, String key, String value, String requestedLanguageId) {
699    
700                    String defaultLanguageId = LocaleUtil.toLanguageId(
701                            LocaleUtil.getDefault());
702    
703                    return updateLocalization(
704                            xml, key, value, requestedLanguageId, defaultLanguageId);
705            }
706    
707            @Override
708            public String updateLocalization(
709                    String xml, String key, String value, String requestedLanguageId,
710                    String defaultLanguageId) {
711    
712                    return updateLocalization(
713                            xml, key, value, requestedLanguageId, defaultLanguageId, false);
714            }
715    
716            @Override
717            public String updateLocalization(
718                    String xml, String key, String value, String requestedLanguageId,
719                    String defaultLanguageId, boolean cdata) {
720    
721                    return updateLocalization(
722                            xml, key, value, requestedLanguageId, defaultLanguageId, cdata,
723                            true);
724            }
725    
726            @Override
727            public String updateLocalization(
728                    String xml, String key, String value, String requestedLanguageId,
729                    String defaultLanguageId, boolean cdata, boolean localized) {
730    
731                    xml = _sanitizeXML(xml);
732    
733                    XMLStreamReader xmlStreamReader = null;
734                    XMLStreamWriter xmlStreamWriter = null;
735    
736                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
737    
738                    ClassLoader contextClassLoader =
739                            ClassLoaderUtil.getContextClassLoader();
740    
741                    try {
742                            if (contextClassLoader != portalClassLoader) {
743                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
744                            }
745    
746                            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
747    
748                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
749                                    new UnsyncStringReader(xml));
750    
751                            String availableLocales = StringPool.BLANK;
752    
753                            // Read root node
754    
755                            if (xmlStreamReader.hasNext()) {
756                                    xmlStreamReader.nextTag();
757    
758                                    availableLocales = xmlStreamReader.getAttributeValue(
759                                            null, _AVAILABLE_LOCALES);
760    
761                                    if (Validator.isNull(availableLocales)) {
762                                            availableLocales = defaultLanguageId;
763                                    }
764    
765                                    if (!availableLocales.contains(requestedLanguageId)) {
766                                            availableLocales = StringUtil.add(
767                                                    availableLocales, requestedLanguageId,
768                                                    StringPool.COMMA);
769                                    }
770                            }
771    
772                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
773    
774                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
775    
776                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
777                                    unsyncStringWriter);
778    
779                            xmlStreamWriter.writeStartDocument();
780                            xmlStreamWriter.writeStartElement(_ROOT);
781    
782                            if (localized) {
783                                    xmlStreamWriter.writeAttribute(
784                                            _AVAILABLE_LOCALES, availableLocales);
785                                    xmlStreamWriter.writeAttribute(
786                                            _DEFAULT_LOCALE, defaultLanguageId);
787                            }
788    
789                            _copyNonExempt(
790                                    xmlStreamReader, xmlStreamWriter, requestedLanguageId,
791                                    defaultLanguageId, cdata);
792    
793                            xmlStreamWriter.writeStartElement(key);
794    
795                            if (localized) {
796                                    xmlStreamWriter.writeAttribute(
797                                            _LANGUAGE_ID, requestedLanguageId);
798                            }
799    
800                            if (cdata) {
801                                    xmlStreamWriter.writeCData(value);
802                            }
803                            else {
804                                    xmlStreamWriter.writeCharacters(value);
805                            }
806    
807                            xmlStreamWriter.writeEndElement();
808                            xmlStreamWriter.writeEndElement();
809                            xmlStreamWriter.writeEndDocument();
810    
811                            xmlStreamWriter.close();
812                            xmlStreamWriter = null;
813    
814                            xml = unsyncStringWriter.toString();
815                    }
816                    catch (Exception e) {
817                            if (_log.isWarnEnabled()) {
818                                    _log.warn(e, e);
819                            }
820                    }
821                    finally {
822                            if (contextClassLoader != portalClassLoader) {
823                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
824                            }
825    
826                            if (xmlStreamReader != null) {
827                                    try {
828                                            xmlStreamReader.close();
829                                    }
830                                    catch (Exception e) {
831                                    }
832                            }
833    
834                            if (xmlStreamWriter != null) {
835                                    try {
836                                            xmlStreamWriter.close();
837                                    }
838                                    catch (Exception e) {
839                                    }
840                            }
841                    }
842    
843                    return xml;
844            }
845    
846            private void _copyNonExempt(
847                            XMLStreamReader xmlStreamReader, XMLStreamWriter xmlStreamWriter,
848                            String exemptLanguageId, String defaultLanguageId, boolean cdata)
849                    throws XMLStreamException {
850    
851                    while (xmlStreamReader.hasNext()) {
852                            int event = xmlStreamReader.next();
853    
854                            if (event == XMLStreamConstants.START_ELEMENT) {
855                                    String languageId = xmlStreamReader.getAttributeValue(
856                                            null, _LANGUAGE_ID);
857    
858                                    if (Validator.isNull(languageId)) {
859                                            languageId = defaultLanguageId;
860                                    }
861    
862                                    if (!languageId.equals(exemptLanguageId)) {
863                                            xmlStreamWriter.writeStartElement(
864                                                    xmlStreamReader.getLocalName());
865                                            xmlStreamWriter.writeAttribute(_LANGUAGE_ID, languageId);
866    
867                                            while (xmlStreamReader.hasNext()) {
868                                                    event = xmlStreamReader.next();
869    
870                                                    if ((event == XMLStreamConstants.CHARACTERS) ||
871                                                            (event == XMLStreamConstants.CDATA)) {
872    
873                                                            String text = xmlStreamReader.getText();
874    
875                                                            if (cdata) {
876                                                                    xmlStreamWriter.writeCData(text);
877                                                            }
878                                                            else {
879                                                                    xmlStreamWriter.writeCharacters(
880                                                                            xmlStreamReader.getText());
881                                                            }
882    
883                                                            break;
884                                                    }
885                                                    else if (event == XMLStreamConstants.END_ELEMENT) {
886                                                            break;
887                                                    }
888                                            }
889    
890                                            xmlStreamWriter.writeEndElement();
891                                    }
892                            }
893                            else if (event == XMLStreamConstants.END_DOCUMENT) {
894                                    break;
895                            }
896                    }
897            }
898    
899            private String _getCachedValue(
900                    String xml, String requestedLanguageId, boolean useDefault) {
901    
902                    String value = null;
903    
904                    Map<Tuple, String> valueMap = _cache.get(xml);
905    
906                    if (valueMap != null) {
907                            Tuple subkey = new Tuple(useDefault, requestedLanguageId);
908    
909                            value = valueMap.get(subkey);
910                    }
911    
912                    return value;
913            }
914    
915            private String _getLocalization(
916                    String bundleName, Locale locale, ClassLoader classLoader, String key,
917                    String defaultValue) {
918    
919                    ResourceBundle resourceBundle = ResourceBundle.getBundle(
920                            bundleName, locale, classLoader);
921    
922                    String value = null;
923    
924                    try {
925                            value = resourceBundle.getString(key);
926    
927                            value = new String(
928                                    value.getBytes(StringPool.ISO_8859_1), StringPool.UTF8);
929                    }
930    
931                    catch (Exception e) {
932                    }
933    
934                    if (Validator.isNotNull(value)) {
935                            value = LanguageResources.fixValue(value);
936                    }
937                    else {
938                            value = LanguageUtil.get(locale, key, defaultValue);
939                    }
940    
941                    return value;
942            }
943    
944            private String _getRootAttribute(
945                    String xml, String name, String defaultValue) {
946    
947                    String value = null;
948    
949                    XMLStreamReader xmlStreamReader = null;
950    
951                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
952    
953                    ClassLoader contextClassLoader =
954                            ClassLoaderUtil.getContextClassLoader();
955    
956                    try {
957                            if (contextClassLoader != portalClassLoader) {
958                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
959                            }
960    
961                            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
962    
963                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
964                                    new UnsyncStringReader(xml));
965    
966                            if (xmlStreamReader.hasNext()) {
967                                    xmlStreamReader.nextTag();
968    
969                                    value = xmlStreamReader.getAttributeValue(null, name);
970                            }
971                    }
972                    catch (Exception e) {
973                            if (_log.isWarnEnabled()) {
974                                    _log.warn(e, e);
975                            }
976                    }
977                    finally {
978                            if (contextClassLoader != portalClassLoader) {
979                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
980                            }
981    
982                            if (xmlStreamReader != null) {
983                                    try {
984                                            xmlStreamReader.close();
985                                    }
986                                    catch (Exception e) {
987                                    }
988                            }
989                    }
990    
991                    if (Validator.isNull(value)) {
992                            value = defaultValue;
993                    }
994    
995                    return value;
996            }
997    
998            private String _sanitizeXML(String xml) {
999                    if (Validator.isNull(xml) || !xml.contains("<root")) {
1000                            xml = _EMPTY_ROOT_NODE;
1001                    }
1002    
1003                    return xml;
1004            }
1005    
1006            private void _setCachedValue(
1007                    String xml, String requestedLanguageId, boolean useDefault,
1008                    String value) {
1009    
1010                    if (Validator.isNotNull(xml) && !xml.equals(_EMPTY_ROOT_NODE)) {
1011                            synchronized (_cache) {
1012                                    Map<Tuple, String> map = _cache.get(xml);
1013    
1014                                    if (map == null) {
1015                                            map = new HashMap<Tuple, String>();
1016                                    }
1017    
1018                                    Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1019    
1020                                    map.put(subkey, value);
1021    
1022                                    _cache.put(xml, map);
1023                            }
1024                    }
1025            }
1026    
1027            private static final String _AVAILABLE_LOCALES = "available-locales";
1028    
1029            private static final String _DEFAULT_LOCALE = "default-locale";
1030    
1031            private static final String _EMPTY_ROOT_NODE = "<root />";
1032    
1033            private static final String _LANGUAGE_ID = "language-id";
1034    
1035            private static final String _ROOT = "root";
1036    
1037            private static Log _log = LogFactoryUtil.getLog(LocalizationImpl.class);
1038    
1039            private Map<String, Map<Tuple, String>> _cache = new ReferenceMap(
1040                    ReferenceMap.SOFT, ReferenceMap.HARD);
1041    
1042    }