OSDN Git Service

* gnu/classpath/Configuration.java.in (INIT_LOAD_LIBRARY): New
[pf3gnuchains/gcc-fork.git] / libjava / java / util / ResourceBundle.java
1 /* java.util.ResourceBundle
2    Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 As a special exception, if you link this library with other files to
22 produce an executable, this library does not by itself cause the
23 resulting executable to be covered by the GNU General Public License.
24 This exception does not however invalidate any other reasons why the
25 executable file might be covered by the GNU General Public License. */
26
27
28 package java.util;
29 import java.lang.ref.Reference;
30 import java.lang.ref.SoftReference;
31 import gnu.classpath.Configuration;
32
33 /**
34  * A resource bundle contains locale-specific data.  If you need
35  * localized data, you can load a resource bundle that matches the
36  * locale with <code>getBundle</code>.  Now you can get your object by
37  * calling <code>getObject</code> or <code>getString</code> on that
38  * bundle.
39  * <br>
40  * When a bundle is demanded for a specific locale, the ResourceBundle
41  * is searched in following order (<i>def. language code<i> stands for
42  * the two letter ISO language code of the default locale (see
43  * <code>Locale.getDefault()</code>).
44  * <pre>
45  * baseName_<i>language code</i>_<i>country code</i>_<i>variant</i>
46  * baseName_<i>language code</i>_<i>country code</i>
47  * baseName_<i>language code</i>
48  * baseName_<i>def. language code</i>_<i>def. country code</i>_<i>def. variant</i>
49  * baseName_<i>def. language code</i>_<i>def. country code</i>
50  * baseName_<i>def. language code</i>
51  * baseName
52  * </pre>
53  *
54  * A bundle is backed up, by less specific bundle (omiting variant,
55  * country or language). But it is not backed up by the default
56  * language locale.
57  * <br>
58  * If you provide a bundle for a given locale, say
59  * <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for
60  * all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and
61  * <code>Bundle</code>.
62  * <br>
63  * When a bundle is searched, we look first for a class with
64  * the given name and if that is not found for a file with
65  * <code>.properties</code> extension in the classpath.  The name
66  * must be a fully qualified classname (with dots as path separators).  
67  * <br>
68  * (Note: This implementation always backs up the class with a
69  * properties file if that is existing, but you shouldn't rely on
70  * this, if you want to be compatible to the standard JDK.)
71  *
72  * @see Locale
73  * @see PropertyResourceBundle
74  * @author Jochen Hoenicke */
75 public abstract class ResourceBundle
76 {
77   static 
78   {
79     if (Configuration.INIT_LOAD_LIBRARY)
80       {
81         System.loadLibrary ("javautil");
82       }
83   }
84
85   /**
86    * The parent bundle.  This is consulted when you call getObject
87    * and there is no such resource in the current bundle.  This
88    * field may be null.  
89    */
90   protected ResourceBundle parent;
91
92   /**
93    * The locale of this resource bundle.  You can read this with
94    * <code>getLocale</code> and it is automatically set in
95    * <code>getBundle</code>.  
96    */
97   private Locale locale;
98
99   /**
100    * The constructor.  It does nothing special.
101    */
102   public ResourceBundle()
103   {
104   }
105
106   /**
107    * Get a String from this resource bundle.  Since most localized
108    * Objects are Strings, this method provides a convenient way to get
109    * them without casting.
110    * @param key the name of the resource.
111    * @exception MissingResourceException
112    *   if that particular object could not be found in this bundle nor
113    *   the parent bundle.
114    */
115   public final String getString(String key) throws MissingResourceException
116   {
117     return (String) getObject(key);
118   }
119
120   /**
121    * Get an array of Strings from this resource bundle.  This method
122    * provides a convenient way to get it without casting.
123    * @param key the name of the resource.
124    * @exception MissingResourceException
125    *   if that particular object could not be found in this bundle nor
126    *   the parent bundle.
127    */
128   public final String[] getStringArray(String key)
129     throws MissingResourceException
130   {
131     return (String[]) getObject(key);
132   }
133
134   /**
135    * Get an object from this resource bundle.
136    * @param key the name of the resource.
137    * @exception MissingResourceException
138    *   if that particular object could not be found in this bundle nor
139    *   the parent bundle.
140    */
141   public final Object getObject(String key) throws MissingResourceException
142   {
143     for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
144       {
145         try
146           {
147             Object o = bundle.handleGetObject(key);
148             if (o != null)
149               return o;
150           }
151         catch (MissingResourceException ex)
152           {
153           }
154       }
155     throw new MissingResourceException
156       ("Key not found", getClass().getName(), key);
157   }
158
159   /**
160    * This method returns an array with the classes of the calling
161    * methods.  The zeroth entry is the class that called this method
162    * (should always be ResourceBundle), the first contains the class
163    * that called the caller (i.e. the class that called getBundle).
164    *
165    * Implementation note: This depends on the fact, that getBundle
166    * doesn't get inlined, but since it calls a private method, it
167    * isn't inlineable.
168    *
169    * @return an array containing the classes for the callers.  
170    */
171   private static native Class[] getClassContext();
172
173   /**
174    * Get the appropriate ResourceBundle for the default locale.  
175    * @param baseName the name of the ResourceBundle.  This should be
176    * a name of a Class or a properties-File.  See the class
177    * description for details.  
178    * @return the desired resource bundle
179    * @exception MissingResourceException 
180    *    if the resource bundle couldn't be found.  */
181   public static final ResourceBundle getBundle(String baseName)
182     throws MissingResourceException
183   {
184     return getBundle(baseName, Locale.getDefault(),
185                      getClassContext()[1].getClassLoader());
186   }
187
188   /**
189    * Get the appropriate ResourceBundle for the given locale.  
190    * @param baseName the name of the ResourceBundle.  This should be
191    * a name of a Class or a properties-File.  See the class
192    * description for details.  
193    * @param locale A locale.
194    * @return the desired resource bundle
195    * @exception MissingResourceException 
196    *    if the resource bundle couldn't be found.
197    */
198   public static final ResourceBundle getBundle(String baseName,
199                                                Locale locale)
200     throws MissingResourceException
201   {
202     return getBundle(baseName, locale, getClassContext()[1].getClassLoader());
203   }
204
205   /**
206    * The resource bundle cache.  This is a two-level hash map: The key
207    * is the class loader, the value is a new HashMap.  The key of this
208    * second hash map is the localized name, the value is a soft
209    * references to the resource bundle.  */
210   private static Map resourceBundleCache = new HashMap();
211
212   /**
213    * The `empty' locale is created once in order to optimize
214    * tryBundle().  
215    */
216   private static final Locale emptyLocale = new Locale ("", "");
217
218   /**
219    * Tries to load a class or a property file with the specified name.
220    * @param localizedName the name.
221    * @param locale the locale, that must be used exactly.
222    * @param classloader the classloader.
223    * @param bundle the back up (parent) bundle
224    * @return the resource bundle if that could be loaded, otherwise 
225    * <code>bundle</code>.
226    */
227   private static final ResourceBundle tryBundle(String localizedName,
228                                                 Locale locale,
229                                                 ClassLoader classloader,
230                                                 ResourceBundle bundle,
231                                                 HashMap cache)
232   {
233     {
234       // First look into the cache.
235       // XXX We should remove cleared references from the cache.
236       Reference ref = (Reference) cache.get(localizedName);
237       if (ref != null)
238         {
239           ResourceBundle rb = (ResourceBundle) ref.get();
240           if (rb != null)
241             // rb should already have the right parent, except if
242             // something very strange happened.
243             return rb;
244         }
245     }
246
247     // foundBundle holds exact matches for the localizedName resource
248     // bundle, which may later be cached.
249     ResourceBundle foundBundle = null;
250
251     try
252       {
253         java.io.InputStream is;
254         final String resourceName =
255           localizedName.replace('.', '/') + ".properties";
256         if (classloader == null)
257           is = ClassLoader.getSystemResourceAsStream (resourceName);
258         else
259           is = classloader.getResourceAsStream (resourceName);
260         if (is != null)
261           {
262             foundBundle = new PropertyResourceBundle(is);
263             foundBundle.parent = bundle;
264             foundBundle.locale = locale;
265           }
266       }
267     catch (java.io.IOException ex)
268       {
269       }
270
271     try
272       {
273         Class rbClass;
274         if (classloader == null)
275           rbClass = Class.forName(localizedName);
276         else
277           rbClass = classloader.loadClass(localizedName);
278         foundBundle = (ResourceBundle) rbClass.newInstance();
279         foundBundle.parent = bundle;
280         foundBundle.locale = locale;
281       }
282     catch (ClassNotFoundException ex)
283       {
284       }
285     catch (IllegalAccessException ex)
286       {
287       }
288     catch (InstantiationException ex)
289       {
290         // ignore them all
291         // XXX should we also ignore ClassCastException?
292       }
293
294     if (foundBundle != null)
295       cache.put(localizedName, new SoftReference(foundBundle));
296
297     return foundBundle != null ? foundBundle : bundle;
298   }
299
300   /**
301    * Tries to load a the bundle for a given locale, also loads the backup
302    * locales with the same language.
303    *
304    * @param name the name.
305    * @param locale the locale, that must be used exactly.
306    * @param classloader the classloader.
307    * @param bundle the back up (parent) bundle
308    * @return the resource bundle if that could be loaded, otherwise 
309    * <code>bundle</code>.
310    */
311   private static final ResourceBundle tryLocalBundle(String baseName,
312                                                      Locale locale,
313                                                      ClassLoader classloader,
314                                                      ResourceBundle bundle,
315                                                      HashMap cache)
316   {
317     final String language = locale.getLanguage();
318
319     if (language.length() > 0)
320       {
321         final String country = locale.getCountry();
322         String name = baseName + "_" + language;
323
324         if (country.length() != 0)
325           {
326             bundle = tryBundle(name,
327                                new Locale(language, ""),
328                                classloader, bundle, cache);
329
330             name += "_" + country;
331
332             final String variant = locale.getVariant();
333
334             if (variant.length() != 0)
335               {
336                 bundle = tryBundle(name,
337                                    new Locale(language,
338                                               country),
339                                    classloader, bundle, cache);
340
341                 name += "_" + variant;
342               }
343           }
344         bundle = tryBundle(name, locale, classloader, bundle, cache);
345       }
346     return bundle;
347   }
348
349   /**
350    * Get the appropriate ResourceBundle for the given locale.  
351    * @param baseName the name of the ResourceBundle.  This should be
352    * a name of a Class or a properties file.  See the class
353    * description for details.  
354    * @param locale A locale.
355    * @param classloader a ClassLoader.
356    * @return the desired resource bundle
357    * @exception MissingResourceException 
358    *    if the resource bundle couldn't be found.
359    */
360   // This method is synchronized so that the cache is properly
361   // handled.
362   public static final synchronized ResourceBundle getBundle(String baseName,
363                                                             Locale locale,
364                                                             ClassLoader classLoader)
365     throws MissingResourceException
366   {
367     // This implementation searches the bundle in the reverse direction
368     // and builds the parent chain on the fly.
369
370     HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
371     if (cache == null)
372       {
373         cache = new HashMap();
374         resourceBundleCache.put(classLoader, cache);
375       }
376     else
377       {
378         // Fast path: If baseName + "_" + locale is in cache use it.
379         String name = baseName + "_" + locale.toString();
380         Reference ref = (Reference) cache.get(name);
381         if (ref != null)
382           {
383             ResourceBundle rb = (ResourceBundle) ref.get();
384             if (rb != null)
385               // rb should already have the right parent, except if
386               // something very strange happened.
387               return rb;
388           }
389       }
390
391     ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,
392                                           classLoader, null, cache);
393     if (baseBundle == null)
394       // JDK says, that if one provides a bundle base_en_UK, one
395       // must also provide the bundles base_en and base.
396       // This implies that if there is no bundle for base, there
397       // is no bundle at all.
398       throw new MissingResourceException("Bundle " + baseName + " not found", baseName, "");
399
400     // Now use the default locale.
401     ResourceBundle bundle = tryLocalBundle(baseName, locale,
402                                            classLoader, baseBundle, cache);
403     if (bundle == baseBundle && !locale.equals(Locale.getDefault()))
404       {
405         bundle = tryLocalBundle(baseName, Locale.getDefault(),
406                                 classLoader, baseBundle, cache);
407       }
408     return bundle;
409   }
410
411   /**
412    * Return the actual locale of this bundle.  You can use it after
413    * calling getBundle, to know if the bundle for the desired locale
414    * was loaded or if the fall back was used.
415    */
416   public Locale getLocale()
417   {
418     return locale;
419   }
420
421   /**
422    * Set the parent of this bundle. This is consulted when you call
423    * getObject and there is no such resource in the current bundle.
424    * @param parent the parent of this bundle.
425    */
426   protected void setParent(ResourceBundle parent)
427   {
428     // Shall we ignore the old parent?
429     this.parent = parent;
430   }
431
432   /**
433    * Override this method to provide the resource for a keys.  This gets
434    * called by <code>getObject</code>.  If you don't have a resource
435    * for the given key, you should return null instead throwing a
436    * MissingResourceException.   You don't have to ask the parent, 
437    * getObject() already does this.
438    *
439    * @param key The key of the resource.
440    * @return The resource for the key, or null if not in bundle.
441    * @exception MissingResourceException
442    *   you shouldn't throw this.
443    */
444   protected abstract Object handleGetObject(String key)
445     throws MissingResourceException;
446
447   /**
448    * This method should return all keys for which a resource exists.
449    * @return An enumeration of the keys.
450    */
451   public abstract Enumeration getKeys();
452 }