1 /* java.util.ResourceBundle
2 Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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. */
29 import java.lang.ref.Reference;
30 import java.lang.ref.SoftReference;
31 import gnu.classpath.Configuration;
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
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>).
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>
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
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>.
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).
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.)
73 * @see PropertyResourceBundle
74 * @author Jochen Hoenicke */
75 public abstract class ResourceBundle
79 if (Configuration.INIT_LOAD_LIBRARY)
81 System.loadLibrary ("javautil");
86 * The parent bundle. This is consulted when you call getObject
87 * and there is no such resource in the current bundle. This
90 protected ResourceBundle parent;
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>.
97 private Locale locale;
100 * The constructor. It does nothing special.
102 public ResourceBundle()
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
115 public final String getString(String key) throws MissingResourceException
117 return (String) getObject(key);
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
128 public final String[] getStringArray(String key)
129 throws MissingResourceException
131 return (String[]) getObject(key);
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
141 public final Object getObject(String key) throws MissingResourceException
143 for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
147 Object o = bundle.handleGetObject(key);
151 catch (MissingResourceException ex)
155 throw new MissingResourceException
156 ("Key not found", getClass().getName(), key);
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).
165 * Implementation note: This depends on the fact, that getBundle
166 * doesn't get inlined, but since it calls a private method, it
169 * @return an array containing the classes for the callers.
171 private static native Class[] getClassContext();
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
184 return getBundle(baseName, Locale.getDefault(),
185 getClassContext()[1].getClassLoader());
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.
198 public static final ResourceBundle getBundle(String baseName,
200 throws MissingResourceException
202 return getBundle(baseName, locale, getClassContext()[1].getClassLoader());
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();
213 * The `empty' locale is created once in order to optimize
216 private static final Locale emptyLocale = new Locale ("", "");
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>.
227 private static final ResourceBundle tryBundle(String localizedName,
229 ClassLoader classloader,
230 ResourceBundle bundle,
234 // First look into the cache.
235 // XXX We should remove cleared references from the cache.
236 Reference ref = (Reference) cache.get(localizedName);
239 ResourceBundle rb = (ResourceBundle) ref.get();
241 // rb should already have the right parent, except if
242 // something very strange happened.
247 // foundBundle holds exact matches for the localizedName resource
248 // bundle, which may later be cached.
249 ResourceBundle foundBundle = null;
253 java.io.InputStream is;
254 final String resourceName =
255 localizedName.replace('.', '/') + ".properties";
256 if (classloader == null)
257 is = ClassLoader.getSystemResourceAsStream (resourceName);
259 is = classloader.getResourceAsStream (resourceName);
262 foundBundle = new PropertyResourceBundle(is);
263 foundBundle.parent = bundle;
264 foundBundle.locale = locale;
267 catch (java.io.IOException ex)
274 if (classloader == null)
275 rbClass = Class.forName(localizedName);
277 rbClass = classloader.loadClass(localizedName);
278 foundBundle = (ResourceBundle) rbClass.newInstance();
279 foundBundle.parent = bundle;
280 foundBundle.locale = locale;
282 catch (ClassNotFoundException ex)
285 catch (IllegalAccessException ex)
288 catch (InstantiationException ex)
291 // XXX should we also ignore ClassCastException?
294 if (foundBundle != null)
295 cache.put(localizedName, new SoftReference(foundBundle));
297 return foundBundle != null ? foundBundle : bundle;
301 * Tries to load a the bundle for a given locale, also loads the backup
302 * locales with the same language.
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>.
311 private static final ResourceBundle tryLocalBundle(String baseName,
313 ClassLoader classloader,
314 ResourceBundle bundle,
317 final String language = locale.getLanguage();
319 if (language.length() > 0)
321 final String country = locale.getCountry();
322 String name = baseName + "_" + language;
324 if (country.length() != 0)
326 bundle = tryBundle(name,
327 new Locale(language, ""),
328 classloader, bundle, cache);
330 name += "_" + country;
332 final String variant = locale.getVariant();
334 if (variant.length() != 0)
336 bundle = tryBundle(name,
339 classloader, bundle, cache);
341 name += "_" + variant;
344 bundle = tryBundle(name, locale, classloader, bundle, cache);
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.
360 // This method is synchronized so that the cache is properly
362 public static final synchronized ResourceBundle getBundle(String baseName,
364 ClassLoader classLoader)
365 throws MissingResourceException
367 // This implementation searches the bundle in the reverse direction
368 // and builds the parent chain on the fly.
370 HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
373 cache = new HashMap();
374 resourceBundleCache.put(classLoader, cache);
378 // Fast path: If baseName + "_" + locale is in cache use it.
379 String name = baseName + "_" + locale.toString();
380 Reference ref = (Reference) cache.get(name);
383 ResourceBundle rb = (ResourceBundle) ref.get();
385 // rb should already have the right parent, except if
386 // something very strange happened.
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, "");
400 // Now use the default locale.
401 ResourceBundle bundle = tryLocalBundle(baseName, locale,
402 classLoader, baseBundle, cache);
403 if (bundle == baseBundle && !locale.equals(Locale.getDefault()))
405 bundle = tryLocalBundle(baseName, Locale.getDefault(),
406 classLoader, baseBundle, cache);
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.
416 public Locale getLocale()
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.
426 protected void setParent(ResourceBundle parent)
428 // Shall we ignore the old parent?
429 this.parent = parent;
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.
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.
444 protected abstract Object handleGetObject(String key)
445 throws MissingResourceException;
448 * This method should return all keys for which a resource exists.
449 * @return An enumeration of the keys.
451 public abstract Enumeration getKeys();