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