1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.util;
16  
17  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
18  import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
19  import com.liferay.portal.kernel.json.JSONObject;
20  import com.liferay.portal.kernel.language.LanguageUtil;
21  import com.liferay.portal.kernel.log.Log;
22  import com.liferay.portal.kernel.log.LogFactoryUtil;
23  import com.liferay.portal.kernel.util.LocaleUtil;
24  import com.liferay.portal.kernel.util.Localization;
25  import com.liferay.portal.kernel.util.ParamUtil;
26  import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.StringUtil;
29  import com.liferay.portal.kernel.util.Tuple;
30  import com.liferay.portal.kernel.util.Validator;
31  
32  import java.util.HashMap;
33  import java.util.Locale;
34  import java.util.Map;
35  
36  import javax.portlet.ActionRequest;
37  import javax.portlet.PortletPreferences;
38  import javax.portlet.PortletRequest;
39  
40  import javax.xml.stream.XMLInputFactory;
41  import javax.xml.stream.XMLOutputFactory;
42  import javax.xml.stream.XMLStreamConstants;
43  import javax.xml.stream.XMLStreamException;
44  import javax.xml.stream.XMLStreamReader;
45  import javax.xml.stream.XMLStreamWriter;
46  
47  import org.apache.commons.collections.map.ReferenceMap;
48  
49  /**
50   * <a href="LocalizationImpl.java.html"><b><i>View Source</i></b></a>
51   *
52   * <p>
53   * This class is used to localize values stored in XML and is often used to add
54   * localization behavior to value objects.
55   * </p>
56   *
57   * <p>
58   * Caching of the localized values is done in this class rather than in the
59   * value object since value objects get flushed from cache fairly quickly.
60   * Though lookups performed on a key based on an XML file is slower than lookups
61   * done at the value object level in general, the value object will get flushed
62   * at a rate which works against the performance gain. The cache is a soft hash
63   * map which prevents memory leaks within the system while enabling the cache to
64   * live longer than in a weak hash map.
65   * </p>
66   *
67   * @author Alexander Chow
68   * @author Jorge Ferrer
69   * @author Mauro Mariuzzo
70   * @author Julio Camarero
71   * @author Brian Wing Shun Chan
72   */
73  public class LocalizationImpl implements Localization {
74  
75      public Object deserialize(JSONObject jsonObject) {
76          Locale[] locales = LanguageUtil.getAvailableLocales();
77  
78          Map<Locale, String> map = new HashMap<Locale, String>();
79  
80          for (Locale locale : locales) {
81              String languageId = LocaleUtil.toLanguageId(locale);
82  
83              String value = jsonObject.getString(languageId);
84  
85              if (Validator.isNotNull(value)) {
86                  map.put(locale, value);
87              }
88          }
89  
90          return map;
91      }
92  
93      public String[] getAvailableLocales(String xml) {
94          String attributeValue = _getRootAttribute(
95              xml, _AVAILABLE_LOCALES, StringPool.BLANK);
96  
97          return StringUtil.split(attributeValue);
98      }
99  
100     public String getDefaultLocale(String xml) {
101         String defaultLanguageId = LocaleUtil.toLanguageId(
102             LocaleUtil.getDefault());
103 
104         return _getRootAttribute(xml, _DEFAULT_LOCALE, defaultLanguageId);
105     }
106 
107     public String getLocalization(String xml, String requestedLanguageId) {
108         return getLocalization(xml, requestedLanguageId, true);
109     }
110 
111     public String getLocalization(
112         String xml, String requestedLanguageId, boolean useDefault) {
113 
114         String value = _getCachedValue(xml, requestedLanguageId, useDefault);
115 
116         if (value != null) {
117             return value;
118         }
119         else {
120             value = StringPool.BLANK;
121         }
122 
123         String systemDefaultLanguageId = LocaleUtil.toLanguageId(
124             LocaleUtil.getDefault());
125 
126         String defaultValue = StringPool.BLANK;
127 
128         if (!Validator.isXml(xml)) {
129             if (requestedLanguageId.equals(systemDefaultLanguageId)) {
130                 value = xml;
131             }
132             else {
133                 value = defaultValue;
134             }
135 
136             _setCachedValue(xml, requestedLanguageId, useDefault, value);
137 
138             return value;
139         }
140 
141         XMLStreamReader xmlStreamReader = null;
142 
143         ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
144 
145         Thread currentThread = Thread.currentThread();
146 
147         ClassLoader contextClassLoader = currentThread.getContextClassLoader();
148 
149         try {
150             if (contextClassLoader != portalClassLoader) {
151                 currentThread.setContextClassLoader(portalClassLoader);
152             }
153 
154             XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
155 
156             xmlStreamReader = xmlInputFactory.createXMLStreamReader(
157                 new UnsyncStringReader(xml));
158 
159             String defaultLanguageId = StringPool.BLANK;
160 
161             // Skip root node
162 
163             if (xmlStreamReader.hasNext()) {
164                 xmlStreamReader.nextTag();
165 
166                 defaultLanguageId = xmlStreamReader.getAttributeValue(
167                     null, _DEFAULT_LOCALE);
168 
169                 if (Validator.isNull(defaultLanguageId)) {
170                     defaultLanguageId = systemDefaultLanguageId;
171                 }
172             }
173 
174             // Find specified language and/or default language
175 
176             while (xmlStreamReader.hasNext()) {
177                 int event = xmlStreamReader.next();
178 
179                 if (event == XMLStreamConstants.START_ELEMENT) {
180                     String languageId = xmlStreamReader.getAttributeValue(
181                         null, _LANGUAGE_ID);
182 
183                     if (Validator.isNull(languageId)) {
184                         languageId = defaultLanguageId;
185                     }
186 
187                     if (languageId.equals(defaultLanguageId) ||
188                         languageId.equals(requestedLanguageId)) {
189 
190                         while (xmlStreamReader.hasNext()) {
191                             event = xmlStreamReader.next();
192 
193                             if (event == XMLStreamConstants.CHARACTERS ||
194                                 event == XMLStreamConstants.CDATA) {
195 
196                                 String text = xmlStreamReader.getText();
197 
198                                 if (languageId.equals(defaultLanguageId)) {
199                                     defaultValue = text;
200                                 }
201 
202                                 if (languageId.equals(requestedLanguageId)) {
203                                     value = text;
204                                 }
205 
206                                 break;
207                             }
208                             else if (event == XMLStreamConstants.END_ELEMENT) {
209                                 break;
210                             }
211                         }
212 
213                         if (Validator.isNotNull(value)) {
214                             break;
215                         }
216                     }
217                 }
218                 else if (event == XMLStreamConstants.END_DOCUMENT) {
219                     break;
220                 }
221             }
222 
223             if (useDefault && Validator.isNull(value)) {
224                 value = defaultValue;
225             }
226         }
227         catch (Exception e) {
228             if (_log.isWarnEnabled()) {
229                 _log.warn(e, e);
230             }
231         }
232         finally {
233             if (contextClassLoader != portalClassLoader) {
234                 currentThread.setContextClassLoader(contextClassLoader);
235             }
236 
237             if (xmlStreamReader != null) {
238                 try {
239                     xmlStreamReader.close();
240                 }
241                 catch (Exception e) {
242                 }
243             }
244         }
245 
246         _setCachedValue(xml, requestedLanguageId, useDefault, value);
247 
248         return value;
249     }
250 
251     public Map<Locale, String> getLocalizationMap(
252         PortletRequest portletRequest, String parameter) {
253 
254         Locale[] locales = LanguageUtil.getAvailableLocales();
255 
256         Map<Locale, String> map = new HashMap<Locale, String>();
257 
258         for (Locale locale : locales) {
259             String languageId = LocaleUtil.toLanguageId(locale);
260 
261             String localeParameter =
262                 parameter + StringPool.UNDERLINE + languageId;
263 
264             map.put(
265                 locale, ParamUtil.getString(portletRequest, localeParameter));
266         }
267 
268         return map;
269     }
270 
271     public Map<Locale, String> getLocalizationMap(String xml) {
272         Locale[] locales = LanguageUtil.getAvailableLocales();
273 
274         Map<Locale, String> map = new HashMap<Locale, String>();
275 
276         for (Locale locale : locales) {
277             String languageId = LocaleUtil.toLanguageId(locale);
278 
279             map.put(locale, getLocalization(xml, languageId));
280         }
281 
282         return map;
283     }
284 
285     /**
286      * @deprecated Use <code>getLocalizationMap</code>.
287      */
288     public Map<Locale, String> getLocalizedParameter(
289         PortletRequest portletRequest, String parameter) {
290 
291         return getLocalizationMap(portletRequest, parameter);
292     }
293 
294     public String getPreferencesValue(
295         PortletPreferences preferences, String key, String languageId) {
296 
297         return getPreferencesValue(preferences, key, languageId, true);
298     }
299 
300     public String getPreferencesValue(
301         PortletPreferences preferences, String key, String languageId,
302         boolean useDefault) {
303 
304         String localizedKey = _getPreferencesKey(key, languageId);
305 
306         String value = preferences.getValue(localizedKey, StringPool.BLANK);
307 
308         if (useDefault && Validator.isNull(value)) {
309             value = preferences.getValue(key, StringPool.BLANK);
310         }
311 
312         return value;
313     }
314 
315     public String[] getPreferencesValues(
316         PortletPreferences preferences, String key, String languageId) {
317 
318         return getPreferencesValues(preferences, key, languageId, true);
319     }
320 
321     public String[] getPreferencesValues(
322         PortletPreferences preferences, String key, String languageId,
323         boolean useDefault) {
324 
325         String localizedKey = _getPreferencesKey(key, languageId);
326 
327         String[] values = preferences.getValues(localizedKey, new String[0]);
328 
329         if (useDefault && Validator.isNull(values)) {
330             values = preferences.getValues(key, new String[0]);
331         }
332 
333         return values;
334     }
335 
336     public String removeLocalization(
337         String xml, String key, String requestedLanguageId) {
338 
339         return removeLocalization(xml, key, requestedLanguageId, false);
340     }
341 
342     public String removeLocalization(
343         String xml, String key, String requestedLanguageId, boolean cdata) {
344 
345         if (Validator.isNull(xml)) {
346             return StringPool.BLANK;
347         }
348 
349         xml = _sanitizeXML(xml);
350 
351         String systemDefaultLanguageId = LocaleUtil.toLanguageId(
352             LocaleUtil.getDefault());
353 
354         XMLStreamReader xmlStreamReader = null;
355         XMLStreamWriter xmlStreamWriter = null;
356 
357         ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
358 
359         Thread currentThread = Thread.currentThread();
360 
361         ClassLoader contextClassLoader = currentThread.getContextClassLoader();
362 
363         try {
364             if (contextClassLoader != portalClassLoader) {
365                 currentThread.setContextClassLoader(portalClassLoader);
366             }
367 
368             XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
369 
370             xmlStreamReader = xmlInputFactory.createXMLStreamReader(
371                 new UnsyncStringReader(xml));
372 
373             String availableLocales = StringPool.BLANK;
374             String defaultLanguageId = StringPool.BLANK;
375 
376             // Read root node
377 
378             if (xmlStreamReader.hasNext()) {
379                 xmlStreamReader.nextTag();
380 
381                 availableLocales = xmlStreamReader.getAttributeValue(
382                     null, _AVAILABLE_LOCALES);
383                 defaultLanguageId = xmlStreamReader.getAttributeValue(
384                     null, _DEFAULT_LOCALE);
385 
386                 if (Validator.isNull(defaultLanguageId)) {
387                     defaultLanguageId = systemDefaultLanguageId;
388                 }
389             }
390 
391             if ((availableLocales != null) &&
392                 (availableLocales.indexOf(requestedLanguageId) != -1)) {
393 
394                 availableLocales = StringUtil.remove(
395                     availableLocales, requestedLanguageId, StringPool.COMMA);
396 
397                 UnsyncStringWriter unsyncStringWriter =
398                     new UnsyncStringWriter();
399 
400                 XMLOutputFactory xmlOutputFactory =
401                     XMLOutputFactory.newInstance();
402 
403                 xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
404                     unsyncStringWriter);
405 
406                 xmlStreamWriter.writeStartDocument();
407                 xmlStreamWriter.writeStartElement(_ROOT);
408                 xmlStreamWriter.writeAttribute(
409                     _AVAILABLE_LOCALES, availableLocales);
410                 xmlStreamWriter.writeAttribute(
411                     _DEFAULT_LOCALE, defaultLanguageId);
412 
413                 _copyNonExempt(
414                     xmlStreamReader, xmlStreamWriter, requestedLanguageId,
415                     defaultLanguageId, cdata);
416 
417                 xmlStreamWriter.writeEndElement();
418                 xmlStreamWriter.writeEndDocument();
419 
420                 xmlStreamWriter.close();
421                 xmlStreamWriter = null;
422 
423                 xml = unsyncStringWriter.toString();
424             }
425         }
426         catch (Exception e) {
427             if (_log.isWarnEnabled()) {
428                 _log.warn(e, e);
429             }
430         }
431         finally {
432             if (contextClassLoader != portalClassLoader) {
433                 currentThread.setContextClassLoader(contextClassLoader);
434             }
435 
436             if (xmlStreamReader != null) {
437                 try {
438                     xmlStreamReader.close();
439                 }
440                 catch (Exception e) {
441                 }
442             }
443 
444             if (xmlStreamWriter != null) {
445                 try {
446                     xmlStreamWriter.close();
447                 }
448                 catch (Exception e) {
449                 }
450             }
451         }
452 
453         return xml;
454     }
455 
456     public void setLocalizedPreferencesValues (
457             ActionRequest actionRequest, PortletPreferences preferences,
458             String parameter)
459         throws Exception {
460 
461         Map<Locale, String> map = getLocalizedParameter(
462             actionRequest, parameter);
463 
464         for (Locale locale : map.keySet()) {
465             String languageId = LocaleUtil.toLanguageId(locale);
466 
467             String key = parameter + StringPool.UNDERLINE + languageId;
468             String value = map.get(locale);
469 
470             preferences.setValue(key, value);
471         }
472     }
473 
474     public void setPreferencesValue(
475             PortletPreferences preferences, String key, String languageId,
476             String value)
477         throws Exception {
478 
479         preferences.setValue(_getPreferencesKey(key, languageId), value);
480     }
481 
482     public void setPreferencesValues(
483             PortletPreferences preferences, String key, String languageId,
484             String[] values)
485         throws Exception {
486 
487         preferences.setValues(_getPreferencesKey(key, languageId), values);
488     }
489 
490     public String updateLocalization(String xml, String key, String value) {
491         String defaultLanguageId = LocaleUtil.toLanguageId(
492             LocaleUtil.getDefault());
493 
494         return updateLocalization(
495             xml, key, value, defaultLanguageId, defaultLanguageId);
496     }
497 
498     public String updateLocalization(
499         String xml, String key, String value, String requestedLanguageId) {
500 
501         String defaultLanguageId = LocaleUtil.toLanguageId(
502             LocaleUtil.getDefault());
503 
504         return updateLocalization(
505             xml, key, value, requestedLanguageId, defaultLanguageId);
506     }
507 
508     public String updateLocalization(
509         String xml, String key, String value, String requestedLanguageId,
510         String defaultLanguageId) {
511 
512         return updateLocalization(
513             xml, key, value, requestedLanguageId, defaultLanguageId, false);
514     }
515 
516     public String updateLocalization(
517         String xml, String key, String value, String requestedLanguageId,
518         String defaultLanguageId, boolean cdata) {
519 
520         xml = _sanitizeXML(xml);
521 
522         XMLStreamReader xmlStreamReader = null;
523         XMLStreamWriter xmlStreamWriter = null;
524 
525         ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
526 
527         Thread currentThread = Thread.currentThread();
528 
529         ClassLoader contextClassLoader = currentThread.getContextClassLoader();
530 
531         try {
532             if (contextClassLoader != portalClassLoader) {
533                 currentThread.setContextClassLoader(portalClassLoader);
534             }
535 
536             XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
537 
538             xmlStreamReader = xmlInputFactory.createXMLStreamReader(
539                 new UnsyncStringReader(xml));
540 
541             String availableLocales = StringPool.BLANK;
542 
543             // Read root node
544 
545             if (xmlStreamReader.hasNext()) {
546                 xmlStreamReader.nextTag();
547 
548                 availableLocales = xmlStreamReader.getAttributeValue(
549                     null, _AVAILABLE_LOCALES);
550 
551                 if (Validator.isNull(availableLocales)) {
552                     availableLocales = defaultLanguageId;
553                 }
554 
555                 if (availableLocales.indexOf(requestedLanguageId) == -1) {
556                     availableLocales = StringUtil.add(
557                         availableLocales, requestedLanguageId,
558                         StringPool.COMMA);
559                 }
560             }
561 
562             UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
563 
564             XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
565 
566             xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(
567                 unsyncStringWriter);
568 
569             xmlStreamWriter.writeStartDocument();
570             xmlStreamWriter.writeStartElement(_ROOT);
571             xmlStreamWriter.writeAttribute(
572                 _AVAILABLE_LOCALES, availableLocales);
573             xmlStreamWriter.writeAttribute(_DEFAULT_LOCALE, defaultLanguageId);
574 
575             _copyNonExempt(
576                 xmlStreamReader, xmlStreamWriter, requestedLanguageId,
577                 defaultLanguageId, cdata);
578 
579             if (cdata) {
580                 xmlStreamWriter.writeStartElement(key);
581                 xmlStreamWriter.writeAttribute(
582                     _LANGUAGE_ID, requestedLanguageId);
583                 xmlStreamWriter.writeCData(value);
584                 xmlStreamWriter.writeEndElement();
585             }
586             else {
587                 xmlStreamWriter.writeStartElement(key);
588                 xmlStreamWriter.writeAttribute(
589                     _LANGUAGE_ID, requestedLanguageId);
590                 xmlStreamWriter.writeCharacters(value);
591                 xmlStreamWriter.writeEndElement();
592             }
593 
594             xmlStreamWriter.writeEndElement();
595             xmlStreamWriter.writeEndDocument();
596 
597             xmlStreamWriter.close();
598             xmlStreamWriter = null;
599 
600             xml = unsyncStringWriter.toString();
601         }
602         catch (Exception e) {
603             if (_log.isWarnEnabled()) {
604                 _log.warn(e, e);
605             }
606         }
607         finally {
608             if (contextClassLoader != portalClassLoader) {
609                 currentThread.setContextClassLoader(contextClassLoader);
610             }
611 
612             if (xmlStreamReader != null) {
613                 try {
614                     xmlStreamReader.close();
615                 }
616                 catch (Exception e) {
617                 }
618             }
619 
620             if (xmlStreamWriter != null) {
621                 try {
622                     xmlStreamWriter.close();
623                 }
624                 catch (Exception e) {
625                 }
626             }
627         }
628 
629         return xml;
630     }
631 
632     private void _copyNonExempt(
633             XMLStreamReader xmlStreamReader, XMLStreamWriter xmlStreamWriter,
634             String exemptLanguageId, String defaultLanguageId, boolean cdata)
635         throws XMLStreamException {
636 
637         while (xmlStreamReader.hasNext()) {
638             int event = xmlStreamReader.next();
639 
640             if (event == XMLStreamConstants.START_ELEMENT) {
641                 String languageId = xmlStreamReader.getAttributeValue(
642                     null, _LANGUAGE_ID);
643 
644                 if (Validator.isNull(languageId)) {
645                     languageId = defaultLanguageId;
646                 }
647 
648                 if (!languageId.equals(exemptLanguageId)) {
649                     xmlStreamWriter.writeStartElement(
650                         xmlStreamReader.getLocalName());
651                     xmlStreamWriter.writeAttribute(_LANGUAGE_ID, languageId);
652 
653                     while (xmlStreamReader.hasNext()) {
654                         event = xmlStreamReader.next();
655 
656                         if (event == XMLStreamConstants.CHARACTERS ||
657                             event == XMLStreamConstants.CDATA) {
658 
659                             String text = xmlStreamReader.getText();
660 
661                             if (cdata) {
662                                 xmlStreamWriter.writeCData(text);
663                             }
664                             else {
665                                 xmlStreamWriter.writeCharacters(
666                                     xmlStreamReader.getText());
667                             }
668 
669                             break;
670                         }
671                         else if (event == XMLStreamConstants.END_ELEMENT) {
672                             break;
673                         }
674                     }
675 
676                     xmlStreamWriter.writeEndElement();
677                 }
678             }
679             else if (event == XMLStreamConstants.END_DOCUMENT) {
680                 break;
681             }
682         }
683     }
684 
685     private String _getCachedValue(
686         String xml, String requestedLanguageId, boolean useDefault) {
687 
688         String value = null;
689 
690         Map<Tuple, String> valueMap = _cache.get(xml);
691 
692         if (valueMap != null) {
693             Tuple subkey = new Tuple(useDefault, requestedLanguageId);
694 
695             value = valueMap.get(subkey);
696         }
697 
698         return value;
699     }
700 
701     private String _getPreferencesKey(String key, String languageId) {
702         String defaultLanguageId = LocaleUtil.toLanguageId(
703             LocaleUtil.getDefault());
704 
705         if (!languageId.equals(defaultLanguageId)) {
706             key += StringPool.UNDERLINE + languageId;
707         }
708 
709         return key;
710     }
711 
712     private String _getRootAttribute(
713         String xml, String name, String defaultValue) {
714 
715         String value = null;
716 
717         XMLStreamReader xmlStreamReader = null;
718 
719         ClassLoader portalClassLoader = PortalClassLoaderUtil.getClassLoader();
720 
721         Thread currentThread = Thread.currentThread();
722 
723         ClassLoader contextClassLoader = currentThread.getContextClassLoader();
724 
725         try {
726             if (contextClassLoader != portalClassLoader) {
727                 currentThread.setContextClassLoader(portalClassLoader);
728             }
729 
730             XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
731 
732             xmlStreamReader = xmlInputFactory.createXMLStreamReader(
733                 new UnsyncStringReader(xml));
734 
735             if (xmlStreamReader.hasNext()) {
736                 xmlStreamReader.nextTag();
737 
738                 value = xmlStreamReader.getAttributeValue(null, name);
739             }
740         }
741         catch (Exception e) {
742             if (_log.isWarnEnabled()) {
743                 _log.warn(e, e);
744             }
745         }
746         finally {
747             if (contextClassLoader != portalClassLoader) {
748                 currentThread.setContextClassLoader(contextClassLoader);
749             }
750 
751             if (xmlStreamReader != null) {
752                 try {
753                     xmlStreamReader.close();
754                 }
755                 catch (Exception e) {
756                 }
757             }
758         }
759 
760         if (Validator.isNull(value)) {
761             value = defaultValue;
762         }
763 
764         return value;
765     }
766 
767     private String _sanitizeXML(String xml) {
768         if (Validator.isNull(xml) || (xml.indexOf("<root") == -1)) {
769             xml = _EMPTY_ROOT_NODE;
770         }
771 
772         return xml;
773     }
774 
775     private void _setCachedValue(
776         String xml, String requestedLanguageId, boolean useDefault,
777         String value) {
778 
779         if (Validator.isNotNull(xml) && !xml.equals(_EMPTY_ROOT_NODE)) {
780             synchronized (_cache) {
781                 Map<Tuple, String> map = _cache.get(xml);
782 
783                 if (map == null) {
784                     map = new HashMap<Tuple, String>();
785                 }
786 
787                 Tuple subkey = new Tuple(useDefault, requestedLanguageId);
788 
789                 map.put(subkey, value);
790 
791                 _cache.put(xml, map);
792             }
793         }
794     }
795 
796     private static final String _AVAILABLE_LOCALES = "available-locales";
797 
798     private static final String _DEFAULT_LOCALE = "default-locale";
799 
800     private static final String _EMPTY_ROOT_NODE = "<root />";
801 
802     private static final String _LANGUAGE_ID = "language-id";
803 
804     private static final String _ROOT = "root";
805 
806     private static Log _log = LogFactoryUtil.getLog(LocalizationImpl.class);
807 
808     private Map<String, Map<Tuple, String>> _cache = new ReferenceMap(
809         ReferenceMap.SOFT, ReferenceMap.HARD);
810 
811 }