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