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