KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > turbine > services > localization > TurbineLocalizationService


1 package org.apache.turbine.services.localization;
2
3 /*
4  * Copyright 2001-2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License")
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.util.HashMap JavaDoc;
20 import java.util.Locale JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.MissingResourceException JavaDoc;
23 import java.util.ResourceBundle JavaDoc;
24
25 import javax.servlet.http.HttpServletRequest JavaDoc;
26
27 import org.apache.commons.configuration.Configuration;
28
29 import org.apache.commons.lang.StringUtils;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import org.apache.turbine.Turbine;
35 import org.apache.turbine.services.InitializationException;
36 import org.apache.turbine.services.TurbineBaseService;
37 import org.apache.turbine.util.RunData;
38
39 /**
40  * <p>This class is the single point of access to all localization
41  * resources. It caches different ResourceBundles for different
42  * Locales.</p>
43  *
44  * <p>Usage example:</p>
45  *
46  * <blockquote><code><pre>
47  * LocalizationService ls = (LocalizationService) TurbineServices
48  * .getInstance().getService(LocalizationService.SERVICE_NAME);
49  * </pre></code></blockquote>
50  *
51  * <p>Then call one of four methods to retrieve a ResourceBundle:
52  *
53  * <ul>
54  * <li>getBundle("MyBundleName")</li>
55  * <li>getBundle("MyBundleName", httpAcceptLanguageHeader)</li>
56  * <li>etBundle("MyBundleName", HttpServletRequest)</li>
57  * <li>getBundle("MyBundleName", Locale)</li>
58  * <li>etc.</li>
59  * </ul></p>
60  *
61  * @author <a HREF="mailto:jm@mediaphil.de">Jonas Maurus</a>
62  * @author <a HREF="mailto:jon@latchkey.com">Jon S. Stevens</a>
63  * @author <a HREF="mailto:novalidemail@foo.com">Frank Y. Kim</a>
64  * @author <a HREF="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
65  * @author <a HREF="mailto:leonardr@collab.net">Leonard Richardson</a>
66  * @author <a HREF="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
67  * @version $Id: TurbineLocalizationService.java,v 1.9.2.2 2004/05/20 03:06:51 seade Exp $
68  */

69 public class TurbineLocalizationService
70         extends TurbineBaseService
71         implements LocalizationService
72 {
73     /** Logging */
74     private static Log log = LogFactory.getLog(TurbineLocalizationService.class);
75
76     /**
77      * The value to pass to <code>MessageFormat</code> if a
78      * <code>null</code> reference is passed to <code>format()</code>.
79      */

80     private static final Object JavaDoc[] NO_ARGS = new Object JavaDoc[0];
81
82     /**
83      * Bundle name keys a Map of the ResourceBundles in this
84      * service (which is in turn keyed by Locale).
85      * Key=bundle name
86      * Value=Hashtable containing ResourceBundles keyed by Locale.
87      */

88     private Map JavaDoc bundles = null;
89
90     /**
91      * The list of default bundles to search.
92      */

93     private String JavaDoc[] bundleNames = null;
94
95     /**
96      * The name of the default locale to use (includes language and
97      * country).
98      */

99     private Locale JavaDoc defaultLocale = null;
100
101     /** The name of the default language to use. */
102     private String JavaDoc defaultLanguage = null;
103
104     /** The name of the default country to use. */
105     private String JavaDoc defaultCountry = null;
106
107     /**
108      * Constructor.
109      */

110     public TurbineLocalizationService()
111     {
112         bundles = new HashMap JavaDoc();
113     }
114
115     /**
116      * Called the first time the Service is used.
117      */

118     public void init()
119             throws InitializationException
120     {
121         Configuration conf = Turbine.getConfiguration();
122
123         initBundleNames(null);
124
125         Locale JavaDoc jvmDefault = Locale.getDefault();
126
127         defaultLanguage = conf.getString("locale.default.language",
128                 jvmDefault.getLanguage()).trim();
129         defaultCountry = conf.getString("locale.default.country",
130                 jvmDefault.getCountry()).trim();
131
132         defaultLocale = new Locale JavaDoc(defaultLanguage, defaultCountry);
133         setInit(true);
134     }
135
136     /**
137      * Initialize list of default bundle names.
138      *
139      * @param ignored Ignored.
140      */

141     protected void initBundleNames(String JavaDoc[] ignored)
142     {
143         Configuration conf = Turbine.getConfiguration();
144         bundleNames = conf.getStringArray("locale.default.bundles");
145         String JavaDoc name = conf.getString("locale.default.bundle");
146
147         if (name != null && name.length() > 0)
148         {
149             // Using old-style single bundle name property.
150
if (bundleNames == null || bundleNames.length <= 0)
151             {
152                 bundleNames = new String JavaDoc[] {name};
153             }
154             else
155             {
156                 // Prepend "default" bundle name.
157
String JavaDoc[] array = new String JavaDoc[bundleNames.length + 1];
158                 array[0] = name;
159                 System.arraycopy(bundleNames, 0, array, 1, bundleNames.length);
160                 bundleNames = array;
161             }
162         }
163         if (bundleNames == null)
164         {
165             bundleNames = new String JavaDoc[0];
166         }
167     }
168
169     /**
170      * Retrieves the default language (specified in the config file).
171      */

172     public String JavaDoc getDefaultLanguage()
173     {
174         return defaultLanguage;
175     }
176
177     /**
178      * Retrieves the default country (specified in the config file).
179      */

180     public String JavaDoc getDefaultCountry()
181     {
182         return defaultCountry;
183     }
184
185     /**
186      * Retrieves the name of the default bundle (as specified in the
187      * config file).
188      * @see org.apache.turbine.services.localization.LocalizationService#getDefaultBundleName()
189      */

190     public String JavaDoc getDefaultBundleName()
191     {
192         return (bundleNames.length > 0 ? bundleNames[0] : "");
193     }
194
195     /**
196      * @see org.apache.turbine.services.localization.LocalizationService#getBundleNames()
197      */

198     public String JavaDoc[] getBundleNames()
199     {
200         return (String JavaDoc []) bundleNames.clone();
201     }
202
203     /**
204      * This method returns a ResourceBundle given the bundle name
205      * "DEFAULT" and the default Locale information supplied in
206      * TurbineProperties.
207      *
208      * @return A localized ResourceBundle.
209      */

210     public ResourceBundle JavaDoc getBundle()
211     {
212         return getBundle(getDefaultBundleName(), (Locale JavaDoc) null);
213     }
214
215     /**
216      * This method returns a ResourceBundle given the bundle name and
217      * the default Locale information supplied in TurbineProperties.
218      *
219      * @param bundleName Name of bundle.
220      * @return A localized ResourceBundle.
221      */

222     public ResourceBundle JavaDoc getBundle(String JavaDoc bundleName)
223     {
224         return getBundle(bundleName, (Locale JavaDoc) null);
225     }
226
227     /**
228      * This method returns a ResourceBundle given the bundle name and
229      * the Locale information supplied in the HTTP "Accept-Language"
230      * header.
231      *
232      * @param bundleName Name of bundle.
233      * @param languageHeader A String with the language header.
234      * @return A localized ResourceBundle.
235      */

236     public ResourceBundle JavaDoc getBundle(String JavaDoc bundleName, String JavaDoc languageHeader)
237     {
238         return getBundle(bundleName, getLocale(languageHeader));
239     }
240
241     /**
242      * This method returns a ResourceBundle given the Locale
243      * information supplied in the HTTP "Accept-Language" header which
244      * is stored in HttpServletRequest.
245      *
246      * @param req HttpServletRequest.
247      * @return A localized ResourceBundle.
248      */

249     public ResourceBundle JavaDoc getBundle(HttpServletRequest JavaDoc req)
250     {
251         return getBundle(getDefaultBundleName(), getLocale(req));
252     }
253
254     /**
255      * This method returns a ResourceBundle given the bundle name and
256      * the Locale information supplied in the HTTP "Accept-Language"
257      * header which is stored in HttpServletRequest.
258      *
259      * @param bundleName Name of the bundle to use if the request's
260      * locale cannot be resolved.
261      * @param req HttpServletRequest.
262      * @return A localized ResourceBundle.
263      */

264     public ResourceBundle JavaDoc getBundle(String JavaDoc bundleName, HttpServletRequest JavaDoc req)
265     {
266         return getBundle(bundleName, getLocale(req));
267     }
268
269     /**
270      * This method returns a ResourceBundle given the Locale
271      * information supplied in the HTTP "Accept-Language" header which
272      * is stored in RunData.
273      *
274      * @param data Turbine information.
275      * @return A localized ResourceBundle.
276      */

277     public ResourceBundle JavaDoc getBundle(RunData data)
278     {
279         return getBundle(getDefaultBundleName(), getLocale(data.getRequest()));
280     }
281
282     /**
283      * This method returns a ResourceBundle given the bundle name and
284      * the Locale information supplied in the HTTP "Accept-Language"
285      * header which is stored in RunData.
286      *
287      * @param bundleName Name of bundle.
288      * @param data Turbine information.
289      * @return A localized ResourceBundle.
290      */

291     public ResourceBundle JavaDoc getBundle(String JavaDoc bundleName, RunData data)
292     {
293         return getBundle(bundleName, getLocale(data.getRequest()));
294     }
295
296     /**
297      * This method returns a ResourceBundle for the given bundle name
298      * and the given Locale.
299      *
300      * @param bundleName Name of bundle (or <code>null</code> for the
301      * default bundle).
302      * @param locale The locale (or <code>null</code> for the locale
303      * indicated by the default language and country).
304      * @return A localized ResourceBundle.
305      */

306     public ResourceBundle JavaDoc getBundle(String JavaDoc bundleName, Locale JavaDoc locale)
307     {
308         // Assure usable inputs.
309
bundleName = (StringUtils.isEmpty(bundleName) ? getDefaultBundleName() : bundleName.trim());
310         if (locale == null)
311         {
312             locale = getLocale((String JavaDoc) null);
313         }
314
315         // Find/retrieve/cache bundle.
316
ResourceBundle JavaDoc rb = null;
317         Map JavaDoc bundlesByLocale = (Map JavaDoc) bundles.get(bundleName);
318         if (bundlesByLocale != null)
319         {
320             // Cache of bundles by locale for the named bundle exists.
321
// Check the cache for a bundle corresponding to locale.
322
rb = (ResourceBundle JavaDoc) bundlesByLocale.get(locale);
323
324             if (rb == null)
325             {
326                 // Not yet cached.
327
rb = cacheBundle(bundleName, locale);
328             }
329         }
330         else
331         {
332             rb = cacheBundle(bundleName, locale);
333         }
334         return rb;
335     }
336
337     /**
338      * Caches the named bundle for fast lookups. This operation is
339      * relatively expesive in terms of memory use, but is optimized
340      * for run-time speed in the usual case.
341      *
342      * @exception MissingResourceException Bundle not found.
343      */

344     private synchronized ResourceBundle JavaDoc cacheBundle(String JavaDoc bundleName,
345                                                     Locale JavaDoc locale)
346         throws MissingResourceException JavaDoc
347     {
348         Map JavaDoc bundlesByLocale = (HashMap JavaDoc) bundles.get(bundleName);
349         ResourceBundle JavaDoc rb = (bundlesByLocale == null ? null :
350                              (ResourceBundle JavaDoc) bundlesByLocale.get(locale));
351
352         if (rb == null)
353         {
354             bundlesByLocale = (bundlesByLocale == null ? new HashMap JavaDoc(3) :
355                                new HashMap JavaDoc(bundlesByLocale));
356             try
357             {
358                 rb = ResourceBundle.getBundle(bundleName, locale);
359             }
360             catch (MissingResourceException JavaDoc e)
361             {
362                 rb = findBundleByLocale(bundleName, locale, bundlesByLocale);
363                 if (rb == null)
364                 {
365                     throw (MissingResourceException JavaDoc) e.fillInStackTrace();
366                 }
367             }
368
369             if (rb != null)
370             {
371                 // Cache bundle.
372
bundlesByLocale.put(rb.getLocale(), rb);
373
374                 Map JavaDoc bundlesByName = new HashMap JavaDoc(bundles);
375                 bundlesByName.put(bundleName, bundlesByLocale);
376                 this.bundles = bundlesByName;
377             }
378         }
379         return rb;
380     }
381
382     /**
383      * <p>Retrieves the bundle most closely matching first against the
384      * supplied inputs, then against the defaults.</p>
385      *
386      * <p>Use case: some clients send a HTTP Accept-Language header
387      * with a value of only the language to use
388      * (i.e. "Accept-Language: en"), and neglect to include a country.
389      * When there is no bundle for the requested language, this method
390      * can be called to try the default country (checking internally
391      * to assure the requested criteria matches the default to avoid
392      * disconnects between language and country).</p>
393      *
394      * <p>Since we're really just guessing at possible bundles to use,
395      * we don't ever throw <code>MissingResourceException</code>.</p>
396      */

397     private ResourceBundle JavaDoc findBundleByLocale(String JavaDoc bundleName, Locale JavaDoc locale,
398                                               Map JavaDoc bundlesByLocale)
399     {
400         ResourceBundle JavaDoc rb = null;
401         if ( !StringUtils.isNotEmpty(locale.getCountry()) &&
402              defaultLanguage.equals(locale.getLanguage()) )
403         {
404             /*
405               log.debug("Requested language '" + locale.getLanguage() +
406               "' matches default: Attempting to guess bundle " +
407               "using default country '" + defaultCountry + '\'');
408             */

409             Locale JavaDoc withDefaultCountry = new Locale JavaDoc(locale.getLanguage(),
410                                                    defaultCountry);
411             rb = (ResourceBundle JavaDoc) bundlesByLocale.get(withDefaultCountry);
412             if (rb == null)
413             {
414                 rb = getBundleIgnoreException(bundleName, withDefaultCountry);
415             }
416         }
417         else if ( !StringUtils.isNotEmpty(locale.getLanguage()) &&
418                   defaultCountry.equals(locale.getCountry()) )
419         {
420             Locale JavaDoc withDefaultLanguage = new Locale JavaDoc(defaultLanguage,
421                                                     locale.getCountry());
422             rb = (ResourceBundle JavaDoc) bundlesByLocale.get(withDefaultLanguage);
423             if (rb == null)
424             {
425                 rb = getBundleIgnoreException(bundleName, withDefaultLanguage);
426             }
427         }
428
429         if (rb == null && !defaultLocale.equals(locale))
430         {
431             rb = getBundleIgnoreException(bundleName, defaultLocale);
432         }
433
434         return rb;
435     }
436
437     /**
438      * Retrieves the bundle using the
439      * <code>ResourceBundle.getBundle(String, Locale)</code> method,
440      * returning <code>null</code> instead of throwing
441      * <code>MissingResourceException</code>.
442      */

443     private final ResourceBundle JavaDoc getBundleIgnoreException(String JavaDoc bundleName,
444                                                           Locale JavaDoc locale)
445     {
446         try
447         {
448             return ResourceBundle.getBundle(bundleName, locale);
449         }
450         catch (MissingResourceException JavaDoc ignored)
451         {
452             return null;
453         }
454     }
455
456     /**
457      * This method sets the name of the first bundle in the search
458      * list (the "default" bundle).
459      *
460      * @param defaultBundle Name of default bundle.
461      */

462     public void setBundle(String JavaDoc defaultBundle)
463     {
464         if (bundleNames.length > 0)
465         {
466             bundleNames[0] = defaultBundle;
467         }
468         else
469         {
470             synchronized (this)
471             {
472                 if (bundleNames.length <= 0)
473                 {
474                     bundleNames = new String JavaDoc[] {defaultBundle};
475                 }
476             }
477         }
478     }
479
480     /**
481      * @see org.apache.turbine.services.localization.LocalizationService#getLocale(HttpServletRequest)
482      */

483     public final Locale JavaDoc getLocale(HttpServletRequest JavaDoc req)
484     {
485         return getLocale(req.getHeader(ACCEPT_LANGUAGE));
486     }
487
488     /**
489      * @see org.apache.turbine.services.localization.LocalizationService#getLocale(String)
490      */

491     public Locale JavaDoc getLocale(String JavaDoc header)
492     {
493         if (!StringUtils.isEmpty(header))
494         {
495             LocaleTokenizer tok = new LocaleTokenizer(header);
496             if (tok.hasNext())
497             {
498                 return (Locale JavaDoc) tok.next();
499             }
500         }
501
502         // Couldn't parse locale.
503
return defaultLocale;
504     }
505
506     /**
507      * @exception MissingResourceException Specified key cannot be matched.
508      * @see org.apache.turbine.services.localization.LocalizationService#getString(String, Locale, String)
509      */

510     public String JavaDoc getString(String JavaDoc bundleName, Locale JavaDoc locale, String JavaDoc key)
511     {
512         String JavaDoc value = null;
513
514         if (locale == null)
515         {
516             locale = getLocale((String JavaDoc) null);
517         }
518
519         // Look for text in requested bundle.
520
ResourceBundle JavaDoc rb = getBundle(bundleName, locale);
521         value = getStringOrNull(rb, key);
522
523         // Look for text in list of default bundles.
524
if (value == null && bundleNames.length > 0)
525         {
526             String JavaDoc name;
527             for (int i = 0; i < bundleNames.length; i++)
528             {
529                 name = bundleNames[i];
530                 //System.out.println("getString(): name=" + name +
531
// ", locale=" + locale + ", i=" + i);
532
if (!name.equals(bundleName))
533                 {
534                     rb = getBundle(name, locale);
535                     value = getStringOrNull(rb, key);
536                     if (value != null)
537                     {
538                         locale = rb.getLocale();
539                         break;
540                     }
541                 }
542             }
543         }
544
545         if (value == null)
546         {
547             String JavaDoc loc = locale.toString();
548             String JavaDoc mesg = LocalizationService.SERVICE_NAME +
549                 " noticed missing resource: " +
550                 "bundleName=" + bundleName + ", locale=" + loc +
551                 ", key=" + key;
552             log.debug(mesg);
553             // Text not found in requested or default bundles.
554
throw new MissingResourceException JavaDoc(mesg, bundleName, key);
555         }
556
557         return value;
558     }
559
560     /**
561      * Gets localized text from a bundle if it's there. Otherwise,
562      * returns <code>null</code> (ignoring a possible
563      * <code>MissingResourceException</code>).
564      */

565     protected final String JavaDoc getStringOrNull(ResourceBundle JavaDoc rb, String JavaDoc key)
566     {
567         if (rb != null)
568         {
569             try
570             {
571                 return rb.getString(key);
572             }
573             catch (MissingResourceException JavaDoc ignored)
574             {
575             }
576         }
577         return null;
578     }
579
580 }
581
Popular Tags