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