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