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