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