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