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.getDefault();
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.getDefault());
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.getDefault());
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.getDefault());
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.getDefault();
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                    String xml = StringPool.BLANK;
480    
481                    Locale[] locales = LanguageUtil.getAvailableLocales();
482                    Locale defaultLocale = LocaleUtil.getDefault();
483                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
484    
485                    for (Locale locale : locales) {
486                            String languageId = LocaleUtil.toLanguageId(locale);
487    
488                            String localizedKey = getPreferencesKey(parameter, languageId);
489    
490                            String value = PrefsParamUtil.getString(
491                                    preferences, portletRequest, localizedKey);
492    
493                            if (Validator.isNotNull(value)) {
494                                    xml = updateLocalization(xml, parameter, value, languageId);
495                            }
496                    }
497    
498                    if (Validator.isNull(getLocalization(xml, defaultLanguageId))) {
499                            String oldValue = PrefsParamUtil.getString(
500                                    preferences, portletRequest, parameter);
501    
502                            if (Validator.isNotNull(oldValue)) {
503                                    xml = updateLocalization(xml, parameter, oldValue);
504                            }
505                    }
506    
507                    return xml;
508            }
509    
510            @Override
511            public Map<Locale, String> getLocalizedParameter(
512                    PortletRequest portletRequest, String parameter) {
513    
514                    return getLocalizationMap(portletRequest, parameter);
515            }
516    
517            @Override
518            public String getPreferencesKey(String key, String languageId) {
519                    String defaultLanguageId = LocaleUtil.toLanguageId(
520                            LocaleUtil.getDefault());
521    
522                    if (!languageId.equals(defaultLanguageId)) {
523                            key += StringPool.UNDERLINE + languageId;
524                    }
525    
526                    return key;
527            }
528    
529            @Override
530            public String getPreferencesValue(
531                    PortletPreferences preferences, String key, String languageId) {
532    
533                    return getPreferencesValue(preferences, key, languageId, true);
534            }
535    
536            @Override
537            public String getPreferencesValue(
538                    PortletPreferences preferences, String key, String languageId,
539                    boolean useDefault) {
540    
541                    String localizedKey = getPreferencesKey(key, languageId);
542    
543                    String value = preferences.getValue(localizedKey, StringPool.BLANK);
544    
545                    if (useDefault && Validator.isNull(value)) {
546                            value = preferences.getValue(key, StringPool.BLANK);
547                    }
548    
549                    return value;
550            }
551    
552            @Override
553            public String[] getPreferencesValues(
554                    PortletPreferences preferences, String key, String languageId) {
555    
556                    return getPreferencesValues(preferences, key, languageId, true);
557            }
558    
559            @Override
560            public String[] getPreferencesValues(
561                    PortletPreferences preferences, String key, String languageId,
562                    boolean useDefault) {
563    
564                    String localizedKey = getPreferencesKey(key, languageId);
565    
566                    String[] values = preferences.getValues(localizedKey, new String[0]);
567    
568                    if (useDefault && ArrayUtil.isEmpty(values)) {
569                            values = preferences.getValues(key, new String[0]);
570                    }
571    
572                    return values;
573            }
574    
575            @Override
576            public String removeLocalization(
577                    String xml, String key, String requestedLanguageId) {
578    
579                    return removeLocalization(xml, key, requestedLanguageId, false);
580            }
581    
582            @Override
583            public String removeLocalization(
584                    String xml, String key, String requestedLanguageId, boolean cdata) {
585    
586                    return removeLocalization(xml, key, requestedLanguageId, cdata, true);
587            }
588    
589            @Override
590            public String removeLocalization(
591                    String xml, String key, String requestedLanguageId, boolean cdata,
592                    boolean localized) {
593    
594                    if (Validator.isNull(xml)) {
595                            return StringPool.BLANK;
596                    }
597    
598                    if (!Validator.isXml(xml)) {
599                            return xml;
600                    }
601    
602                    xml = _sanitizeXML(xml);
603    
604                    String systemDefaultLanguageId = LocaleUtil.toLanguageId(
605                            LocaleUtil.getDefault());
606    
607                    XMLStreamReader xmlStreamReader = null;
608                    XMLStreamWriter xmlStreamWriter = null;
609    
610                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
611    
612                    ClassLoader contextClassLoader =
613                            ClassLoaderUtil.getContextClassLoader();
614    
615                    try {
616                            if (contextClassLoader != portalClassLoader) {
617                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
618                            }
619    
620                            XMLInputFactory xmlInputFactory =
621                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
622    
623                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
624                                    new UnsyncStringReader(xml));
625    
626                            String availableLocales = StringPool.BLANK;
627                            String defaultLanguageId = StringPool.BLANK;
628    
629                            // Read root node
630    
631                            if (xmlStreamReader.hasNext()) {
632                                    xmlStreamReader.nextTag();
633    
634                                    availableLocales = xmlStreamReader.getAttributeValue(
635                                            null, _AVAILABLE_LOCALES);
636                                    defaultLanguageId = xmlStreamReader.getAttributeValue(
637                                            null, _DEFAULT_LOCALE);
638    
639                                    if (Validator.isNull(defaultLanguageId)) {
640                                            defaultLanguageId = systemDefaultLanguageId;
641                                    }
642                            }
643    
644                            if ((availableLocales != null) &&
645                                    availableLocales.contains(requestedLanguageId)) {
646    
647                                    availableLocales = StringUtil.remove(
648                                            availableLocales, requestedLanguageId, StringPool.COMMA);
649    
650                                    UnsyncStringWriter unsyncStringWriter =
651                                            new UnsyncStringWriter();
652    
653                                    XMLOutputFactory xmlOutputFactory =
654                                            XMLOutputFactory.newInstance();
655    
656                                    xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
657                                            unsyncStringWriter);
658    
659                                    xmlStreamWriter.writeStartDocument();
660                                    xmlStreamWriter.writeStartElement(_ROOT);
661    
662                                    if (localized) {
663                                            xmlStreamWriter.writeAttribute(
664                                                    _AVAILABLE_LOCALES, availableLocales);
665                                            xmlStreamWriter.writeAttribute(
666                                                    _DEFAULT_LOCALE, defaultLanguageId);
667                                    }
668    
669                                    _copyNonExempt(
670                                            xmlStreamReader, xmlStreamWriter, requestedLanguageId,
671                                            defaultLanguageId, cdata);
672    
673                                    xmlStreamWriter.writeEndElement();
674                                    xmlStreamWriter.writeEndDocument();
675    
676                                    xmlStreamWriter.close();
677                                    xmlStreamWriter = null;
678    
679                                    xml = unsyncStringWriter.toString();
680                            }
681                    }
682                    catch (Exception e) {
683                            if (_log.isWarnEnabled()) {
684                                    _log.warn(e, e);
685                            }
686                    }
687                    finally {
688                            if (contextClassLoader != portalClassLoader) {
689                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
690                            }
691    
692                            if (xmlStreamReader != null) {
693                                    try {
694                                            xmlStreamReader.close();
695                                    }
696                                    catch (Exception e) {
697                                    }
698                            }
699    
700                            if (xmlStreamWriter != null) {
701                                    try {
702                                            xmlStreamWriter.close();
703                                    }
704                                    catch (Exception e) {
705                                    }
706                            }
707                    }
708    
709                    return xml;
710            }
711    
712            @Override
713            public void setLocalizedPreferencesValues(
714                            PortletRequest portletRequest, PortletPreferences preferences,
715                            String parameter)
716                    throws Exception {
717    
718                    Map<Locale, String> map = getLocalizationMap(portletRequest, parameter);
719    
720                    for (Map.Entry<Locale, String> entry : map.entrySet()) {
721                            String languageId = LocaleUtil.toLanguageId(entry.getKey());
722                            String value = entry.getValue();
723    
724                            setPreferencesValue(preferences, parameter, languageId, value);
725                    }
726            }
727    
728            @Override
729            public void setPreferencesValue(
730                            PortletPreferences preferences, String key, String languageId,
731                            String value)
732                    throws Exception {
733    
734                    preferences.setValue(getPreferencesKey(key, languageId), value);
735            }
736    
737            @Override
738            public void setPreferencesValues(
739                            PortletPreferences preferences, String key, String languageId,
740                            String[] values)
741                    throws Exception {
742    
743                    preferences.setValues(getPreferencesKey(key, languageId), values);
744            }
745    
746            @Override
747            public String updateLocalization(
748                    Map<Locale, String> localizationMap, String xml, String key,
749                    String defaultLanguageId) {
750    
751                    Locale[] locales = LanguageUtil.getAvailableLocales();
752    
753                    for (Locale locale : locales) {
754                            String value = localizationMap.get(locale);
755    
756                            String languageId = LocaleUtil.toLanguageId(locale);
757    
758                            if (Validator.isNotNull(value)) {
759                                    xml = updateLocalization(
760                                            xml, key, value, languageId, defaultLanguageId);
761                            }
762                            else {
763                                    xml = removeLocalization(xml, key, languageId);
764                            }
765                    }
766    
767                    return xml;
768            }
769    
770            @Override
771            public String updateLocalization(String xml, String key, String value) {
772                    String defaultLanguageId = LocaleUtil.toLanguageId(
773                            LocaleUtil.getDefault());
774    
775                    return updateLocalization(
776                            xml, key, value, defaultLanguageId, defaultLanguageId);
777            }
778    
779            @Override
780            public String updateLocalization(
781                    String xml, String key, String value, String requestedLanguageId) {
782    
783                    String defaultLanguageId = LocaleUtil.toLanguageId(
784                            LocaleUtil.getDefault());
785    
786                    return updateLocalization(
787                            xml, key, value, requestedLanguageId, defaultLanguageId);
788            }
789    
790            @Override
791            public String updateLocalization(
792                    String xml, String key, String value, String requestedLanguageId,
793                    String defaultLanguageId) {
794    
795                    return updateLocalization(
796                            xml, key, value, requestedLanguageId, defaultLanguageId, false);
797            }
798    
799            @Override
800            public String updateLocalization(
801                    String xml, String key, String value, String requestedLanguageId,
802                    String defaultLanguageId, boolean cdata) {
803    
804                    return updateLocalization(
805                            xml, key, value, requestedLanguageId, defaultLanguageId, cdata,
806                            true);
807            }
808    
809            @Override
810            public String updateLocalization(
811                    String xml, String key, String value, String requestedLanguageId,
812                    String defaultLanguageId, boolean cdata, boolean localized) {
813    
814                    xml = _sanitizeXML(xml);
815    
816                    XMLStreamReader xmlStreamReader = null;
817                    XMLStreamWriter xmlStreamWriter = null;
818    
819                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
820    
821                    ClassLoader contextClassLoader =
822                            ClassLoaderUtil.getContextClassLoader();
823    
824                    try {
825                            if (contextClassLoader != portalClassLoader) {
826                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
827                            }
828    
829                            XMLInputFactory xmlInputFactory =
830                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
831    
832                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
833                                    new UnsyncStringReader(xml));
834    
835                            String availableLocales = StringPool.BLANK;
836    
837                            // Read root node
838    
839                            if (xmlStreamReader.hasNext()) {
840                                    xmlStreamReader.nextTag();
841    
842                                    availableLocales = xmlStreamReader.getAttributeValue(
843                                            null, _AVAILABLE_LOCALES);
844    
845                                    if (Validator.isNull(availableLocales)) {
846                                            availableLocales = defaultLanguageId;
847                                    }
848    
849                                    if (!availableLocales.contains(requestedLanguageId)) {
850                                            availableLocales = StringUtil.add(
851                                                    availableLocales, requestedLanguageId,
852                                                    StringPool.COMMA);
853                                    }
854                            }
855    
856                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
857    
858                            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
859    
860                            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
861                                    unsyncStringWriter);
862    
863                            xmlStreamWriter.writeStartDocument();
864                            xmlStreamWriter.writeStartElement(_ROOT);
865    
866                            if (localized) {
867                                    xmlStreamWriter.writeAttribute(
868                                            _AVAILABLE_LOCALES, availableLocales);
869                                    xmlStreamWriter.writeAttribute(
870                                            _DEFAULT_LOCALE, defaultLanguageId);
871                            }
872    
873                            _copyNonExempt(
874                                    xmlStreamReader, xmlStreamWriter, requestedLanguageId,
875                                    defaultLanguageId, cdata);
876    
877                            xmlStreamWriter.writeStartElement(key);
878    
879                            if (localized) {
880                                    xmlStreamWriter.writeAttribute(
881                                            _LANGUAGE_ID, requestedLanguageId);
882                            }
883    
884                            if (cdata) {
885                                    xmlStreamWriter.writeCData(value);
886                            }
887                            else {
888                                    xmlStreamWriter.writeCharacters(value);
889                            }
890    
891                            xmlStreamWriter.writeEndElement();
892                            xmlStreamWriter.writeEndElement();
893                            xmlStreamWriter.writeEndDocument();
894    
895                            xmlStreamWriter.close();
896                            xmlStreamWriter = null;
897    
898                            xml = unsyncStringWriter.toString();
899                    }
900                    catch (Exception e) {
901                            if (_log.isWarnEnabled()) {
902                                    _log.warn(e, e);
903                            }
904                    }
905                    finally {
906                            if (contextClassLoader != portalClassLoader) {
907                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
908                            }
909    
910                            if (xmlStreamReader != null) {
911                                    try {
912                                            xmlStreamReader.close();
913                                    }
914                                    catch (Exception e) {
915                                    }
916                            }
917    
918                            if (xmlStreamWriter != null) {
919                                    try {
920                                            xmlStreamWriter.close();
921                                    }
922                                    catch (Exception e) {
923                                    }
924                            }
925                    }
926    
927                    return xml;
928            }
929    
930            private void _copyNonExempt(
931                            XMLStreamReader xmlStreamReader, XMLStreamWriter xmlStreamWriter,
932                            String exemptLanguageId, String defaultLanguageId, boolean cdata)
933                    throws XMLStreamException {
934    
935                    while (xmlStreamReader.hasNext()) {
936                            int event = xmlStreamReader.next();
937    
938                            if (event == XMLStreamConstants.START_ELEMENT) {
939                                    String languageId = xmlStreamReader.getAttributeValue(
940                                            null, _LANGUAGE_ID);
941    
942                                    if (Validator.isNull(languageId)) {
943                                            languageId = defaultLanguageId;
944                                    }
945    
946                                    if (!languageId.equals(exemptLanguageId)) {
947                                            xmlStreamWriter.writeStartElement(
948                                                    xmlStreamReader.getLocalName());
949                                            xmlStreamWriter.writeAttribute(_LANGUAGE_ID, languageId);
950    
951                                            while (xmlStreamReader.hasNext()) {
952                                                    event = xmlStreamReader.next();
953    
954                                                    if ((event == XMLStreamConstants.CHARACTERS) ||
955                                                            (event == XMLStreamConstants.CDATA)) {
956    
957                                                            String text = xmlStreamReader.getText();
958    
959                                                            if (cdata) {
960                                                                    xmlStreamWriter.writeCData(text);
961                                                            }
962                                                            else {
963                                                                    xmlStreamWriter.writeCharacters(
964                                                                            xmlStreamReader.getText());
965                                                            }
966    
967                                                            break;
968                                                    }
969                                                    else if (event == XMLStreamConstants.END_ELEMENT) {
970                                                            break;
971                                                    }
972                                            }
973    
974                                            xmlStreamWriter.writeEndElement();
975                                    }
976                            }
977                            else if (event == XMLStreamConstants.END_DOCUMENT) {
978                                    break;
979                            }
980                    }
981            }
982    
983            private String _getCachedValue(
984                    String xml, String requestedLanguageId, boolean useDefault) {
985    
986                    String value = null;
987    
988                    Map<Tuple, String> valueMap = _cache.get(xml);
989    
990                    if (valueMap != null) {
991                            Tuple subkey = new Tuple(useDefault, requestedLanguageId);
992    
993                            value = valueMap.get(subkey);
994                    }
995    
996                    return value;
997            }
998    
999            private String _getLocalization(
1000                    String bundleName, Locale locale, ClassLoader classLoader, String key,
1001                    String defaultValue) {
1002    
1003                    ResourceBundle resourceBundle = ResourceBundle.getBundle(
1004                            bundleName, locale, classLoader);
1005    
1006                    String value = null;
1007    
1008                    try {
1009                            value = resourceBundle.getString(key);
1010    
1011                            value = new String(
1012                                    value.getBytes(StringPool.ISO_8859_1), StringPool.UTF8);
1013                    }
1014    
1015                    catch (Exception e) {
1016                    }
1017    
1018                    if (Validator.isNotNull(value)) {
1019                            value = LanguageResources.fixValue(value);
1020                    }
1021                    else {
1022                            value = LanguageUtil.get(locale, key, defaultValue);
1023                    }
1024    
1025                    return value;
1026            }
1027    
1028            private String _getRootAttributeValue(
1029                    Document document, String name, String defaultValue) {
1030    
1031                    Element rootElement = document.getRootElement();
1032    
1033                    return rootElement.attributeValue(name, defaultValue);
1034            }
1035    
1036            private String _getRootAttributeValue(
1037                    String xml, String name, String defaultValue) {
1038    
1039                    String value = null;
1040    
1041                    XMLStreamReader xmlStreamReader = null;
1042    
1043                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
1044    
1045                    ClassLoader contextClassLoader =
1046                            ClassLoaderUtil.getContextClassLoader();
1047    
1048                    try {
1049                            if (contextClassLoader != portalClassLoader) {
1050                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
1051                            }
1052    
1053                            XMLInputFactory xmlInputFactory =
1054                                    SecureXMLFactoryProviderUtil.newXMLInputFactory();
1055    
1056                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
1057                                    new UnsyncStringReader(xml));
1058    
1059                            if (xmlStreamReader.hasNext()) {
1060                                    xmlStreamReader.nextTag();
1061    
1062                                    value = xmlStreamReader.getAttributeValue(null, name);
1063                            }
1064                    }
1065                    catch (Exception e) {
1066                            if (_log.isWarnEnabled()) {
1067                                    _log.warn(e, e);
1068                            }
1069                    }
1070                    finally {
1071                            if (contextClassLoader != portalClassLoader) {
1072                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
1073                            }
1074    
1075                            if (xmlStreamReader != null) {
1076                                    try {
1077                                            xmlStreamReader.close();
1078                                    }
1079                                    catch (Exception e) {
1080                                    }
1081                            }
1082                    }
1083    
1084                    if (Validator.isNull(value)) {
1085                            value = defaultValue;
1086                    }
1087    
1088                    return value;
1089            }
1090    
1091            private String _sanitizeXML(String xml) {
1092                    if (Validator.isNull(xml) || !xml.contains("<root")) {
1093                            xml = _EMPTY_ROOT_NODE;
1094                    }
1095    
1096                    return xml;
1097            }
1098    
1099            private void _setCachedValue(
1100                    String xml, String requestedLanguageId, boolean useDefault,
1101                    String value) {
1102    
1103                    if (Validator.isNotNull(xml) && !xml.equals(_EMPTY_ROOT_NODE)) {
1104                            synchronized (_cache) {
1105                                    Map<Tuple, String> map = _cache.get(xml);
1106    
1107                                    if (map == null) {
1108                                            map = new HashMap<Tuple, String>();
1109                                    }
1110    
1111                                    Tuple subkey = new Tuple(useDefault, requestedLanguageId);
1112    
1113                                    map.put(subkey, value);
1114    
1115                                    _cache.put(xml, map);
1116                            }
1117                    }
1118            }
1119    
1120            private static final String _AVAILABLE_LOCALES = "available-locales";
1121    
1122            private static final String _DEFAULT_LOCALE = "default-locale";
1123    
1124            private static final String _EMPTY_ROOT_NODE = "<root />";
1125    
1126            private static final String _LANGUAGE_ID = "language-id";
1127    
1128            private static final String _ROOT = "root";
1129    
1130            private static Log _log = LogFactoryUtil.getLog(LocalizationImpl.class);
1131    
1132            private Map<String, Map<Tuple, String>> _cache = new ReferenceMap(
1133                    ReferenceMap.SOFT, ReferenceMap.HARD);
1134    
1135    }