-/* java.util.ResourceBundle
- Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+/* ResourceBundle -- aids in loading resource bundles
+ Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GNU Classpath.
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-As a special exception, if you link this library with other files to
-produce an executable, this library does not by itself cause the
-resulting executable to be covered by the GNU General Public License.
-This exception does not however invalidate any other reasons why the
-executable file might be covered by the GNU General Public License. */
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
package java.util;
+
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
+import java.io.InputStream;
+import java.io.IOException;
+import gnu.classpath.Configuration;
/**
- * A resource bundle contains locale-specific data. If you need
- * localized data, you can load a resource bundle that matches the
- * locale with <code>getBundle</code>. Now you can get your object by
- * calling <code>getObject</code> or <code>getString</code> on that
- * bundle.
- * <br>
- * When a bundle is demanded for a specific locale, the ResourceBundle
- * is searched in following order (<i>def. language code<i> stands for
- * the two letter ISO language code of the default locale (see
+ * A resource bundle contains locale-specific data. If you need localized
+ * data, you can load a resource bundle that matches the locale with
+ * <code>getBundle</code>. Now you can get your object by calling
+ * <code>getObject</code> or <code>getString</code> on that bundle.
+ *
+ * <p>When a bundle is demanded for a specific locale, the ResourceBundle
+ * is searched in following order (<i>def. language<i> stands for the
+ * two letter ISO language code of the default locale (see
* <code>Locale.getDefault()</code>).
- * <pre>
- * baseName_<i>language code</i>_<i>country code</i>_<i>variant</i>
- * baseName_<i>language code</i>_<i>country code</i>
- * baseName_<i>language code</i>
- * baseName_<i>def. language code</i>_<i>def. country code</i>_<i>def. variant</i>
- * baseName_<i>def. language code</i>_<i>def. country code</i>
- * baseName_<i>def. language code</i>
- * baseName
- * </pre>
*
- * A bundle is backed up, by less specific bundle (omiting variant,
- * country or language). But it is not backed up by the default
- * language locale.
- * <br>
- * If you provide a bundle for a given locale, say
+<pre>baseName_<i>language code</i>_<i>country code</i>_<i>variant</i>
+baseName_<i>language code</i>_<i>country code</i>
+baseName_<i>language code</i>
+baseName_<i>def. language</i>_<i>def. country</i>_<i>def. variant</i>
+baseName_<i>def. language</i>_<i>def. country</i>
+baseName_<i>def. language</i>
+baseName</pre>
+ *
+ * <p>A bundle is backed up by less specific bundles (omitting variant, country
+ * or language). But it is not backed up by the default language locale.
+ *
+ * <p>If you provide a bundle for a given locale, say
* <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for
* all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and
* <code>Bundle</code>.
- * <br>
- * When a bundle is searched, we look first for a class with
- * the given name and if that is not found for a file with
- * <code>.properties</code> extension in the classpath. The name
- * must be a fully qualified classname (with dots as path separators).
- * <br>
- * (Note: This implementation always backs up the class with a
- * properties file if that is existing, but you shouldn't rely on
- * this, if you want to be compatible to the standard JDK.)
*
+ * <p>When a bundle is searched, we look first for a class with the given
+ * name, then for a file with <code>.properties</code> extension in the
+ * classpath. The name must be a fully qualified classname (with dots as
+ * path separators).
+ *
+ * <p>(Note: This implementation always backs up the class with a properties
+ * file if that is existing, but you shouldn't rely on this, if you want to
+ * be compatible to the standard JDK.)
+ *
+ * @author Jochen Hoenicke
+ * @author Eric Blake (ebb9@email.byu.edu)
* @see Locale
+ * @see ListResourceBundle
* @see PropertyResourceBundle
- * @author Jochen Hoenicke */
+ * @since 1.1
+ * @status updated to 1.4
+ */
public abstract class ResourceBundle
{
/**
- * The parent bundle. This is consulted when you call getObject
- * and there is no such resource in the current bundle. This
- * field may be null.
+ * The parent bundle. This is consulted when you call getObject and there
+ * is no such resource in the current bundle. This field may be null.
*/
protected ResourceBundle parent;
/**
- * The locale of this resource bundle. You can read this with
+ * The locale of this resource bundle. You can read this with
* <code>getLocale</code> and it is automatically set in
- * <code>getBundle</code>.
+ * <code>getBundle</code>.
*/
private Locale locale;
+ private static native ClassLoader getCallingClassLoader();
+
+ /**
+ * The resource bundle cache. This is a two-level hash map: The key
+ * is the class loader, the value is a new HashMap. The key of this
+ * second hash map is the localized name, the value is a soft
+ * references to the resource bundle.
+ */
+ private static Map resourceBundleCache;
+
/**
- * The constructor. It does nothing special.
+ * The last default Locale we saw. If this ever changes then we have to
+ * reset our caches.
+ */
+ private static Locale lastDefaultLocale;
+
+ /**
+ * The `empty' locale is created once in order to optimize
+ * tryBundle().
+ */
+ private static final Locale emptyLocale = new Locale("");
+
+ /**
+ * The constructor. It does nothing special.
*/
public ResourceBundle()
{
}
/**
- * Get a String from this resource bundle. Since most localized
- * Objects are Strings, this method provides a convenient way to get
- * them without casting.
- * @param key the name of the resource.
- * @exception MissingResourceException
- * if that particular object could not be found in this bundle nor
- * the parent bundle.
+ * Get a String from this resource bundle. Since most localized Objects
+ * are Strings, this method provides a convenient way to get them without
+ * casting.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
+ * @throws ClassCastException if resource is not a string
*/
- public final String getString(String key) throws MissingResourceException
+ public final String getString(String key)
{
return (String) getObject(key);
}
/**
- * Get an array of Strings from this resource bundle. This method
+ * Get an array of Strings from this resource bundle. This method
* provides a convenient way to get it without casting.
- * @param key the name of the resource.
- * @exception MissingResourceException
- * if that particular object could not be found in this bundle nor
- * the parent bundle.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
+ * @throws ClassCastException if resource is not a string
*/
public final String[] getStringArray(String key)
- throws MissingResourceException
{
return (String[]) getObject(key);
}
/**
- * Get an object from this resource bundle.
- * @param key the name of the resource.
- * @exception MissingResourceException
- * if that particular object could not be found in this bundle nor
- * the parent bundle.
+ * Get an object from this resource bundle. This will call
+ * <code>handleGetObject</code> for this resource and all of its parents,
+ * until it finds a non-null resource.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
*/
- public final Object getObject(String key) throws MissingResourceException
+ public final Object getObject(String key)
{
for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
- {
- try
- {
- Object o = bundle.handleGetObject(key);
- if (o != null)
- return o;
- }
- catch (MissingResourceException ex)
- {
- }
- }
- throw new MissingResourceException
- ("Key not found", getClass().getName(), key);
+ try
+ {
+ Object o = bundle.handleGetObject(key);
+ if (o != null)
+ return o;
+ }
+ catch (MissingResourceException ex)
+ {
+ }
+
+ throw new MissingResourceException("Key not found", getClass().getName(),
+ key);
}
/**
- * This method returns an array with the classes of the calling
- * methods. The zeroth entry is the class that called this method
- * (should always be ResourceBundle), the first contains the class
- * that called the caller (i.e. the class that called getBundle).
+ * Return the actual locale of this bundle. You can use it after calling
+ * getBundle, to know if the bundle for the desired locale was loaded or
+ * if the fall back was used.
*
- * Implementation note: This depends on the fact, that getBundle
- * doesn't get inlined, but since it calls a private method, it
- * isn't inlineable.
+ * @return the bundle's locale
+ */
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ /**
+ * Set the parent of this bundle. The parent is consulted when you call
+ * getObject and there is no such resource in the current bundle.
*
- * @return an array containing the classes for the callers.
+ * @param parent the parent of this bundle
*/
- private static native Class[] getClassContext();
+ protected void setParent(ResourceBundle parent)
+ {
+ this.parent = parent;
+ }
/**
- * Get the appropriate ResourceBundle for the default locale.
- * @param baseName the name of the ResourceBundle. This should be
- * a name of a Class or a properties-File. See the class
- * description for details.
+ * Get the appropriate ResourceBundle for the default locale. This is like
+ * calling <code>getBundle(baseName, Locale.getDefault(),
+ * getClass().getClassLoader()</code>, except that any security check of
+ * getClassLoader won't fail.
+ *
+ * @param baseName the name of the ResourceBundle
* @return the desired resource bundle
- * @exception MissingResourceException
- * if the resource bundle couldn't be found. */
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if baseName is null
+ */
public static final ResourceBundle getBundle(String baseName)
- throws MissingResourceException
{
return getBundle(baseName, Locale.getDefault(),
- getClassContext()[1].getClassLoader());
+ getCallingClassLoader());
}
/**
- * Get the appropriate ResourceBundle for the given locale.
- * @param baseName the name of the ResourceBundle. This should be
- * a name of a Class or a properties-File. See the class
- * description for details.
- * @param locale A locale.
+ * Get the appropriate ResourceBundle for the given locale. This is like
+ * calling <code>getBundle(baseName, locale,
+ * getClass().getClassLoader()</code>, except that any security check of
+ * getClassLoader won't fail.
+ *
+ * @param baseName the name of the ResourceBundle
+ * @param locale A locale
* @return the desired resource bundle
- * @exception MissingResourceException
- * if the resource bundle couldn't be found.
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if baseName or locale is null
*/
public static final ResourceBundle getBundle(String baseName,
- Locale locale)
- throws MissingResourceException
+ Locale locale)
{
- return getBundle(baseName, locale, getClassContext()[1].getClassLoader());
+ return getBundle(baseName, locale, getCallingClassLoader());
}
/**
- * The resource bundle cache. This is a two-level hash map: The key
- * is the class loader, the value is a new HashMap. The key of this
- * second hash map is the localized name, the value is a soft
- * references to the resource bundle. */
- private static Map resourceBundleCache = new HashMap();
+ * Get the appropriate ResourceBundle for the given locale. The following
+ * strategy is used:
+ *
+ * <p>A sequence of candidate bundle names are generated, and tested in
+ * this order, where the suffix 1 means the string from the specified
+ * locale, and the suffix 2 means the string from the default locale:<ul>
+ * <li>baseName + "_" + language1 + "_" + country1 + "_" + variant1</li>
+ * <li>baseName + "_" + language1 + "_" + country1</li>
+ * <li>baseName + "_" + language1</li>
+ * <li>baseName + "_" + language2 + "_" + country2 + "_" + variant2</li>
+ * <li>baseName + "_" + language2 + "_" + country2</li>
+ * <li>baseName + "_" + language2<li>
+ * <li>baseName</li>
+ * </ul>
+ *
+ * <p>In the sequence, entries with an empty string are ignored. Next,
+ * <code>getBundle</code> tries to instantiate the resource bundle:<ul>
+ * <li>First, an attempt is made to load a class in the specified classloader
+ * which is a subclass of ResourceBundle, and which has a public constructor
+ * with no arguments, via reflection.</li>
+ * <li>Next, a search is made for a property resource file, by replacing
+ * '.' with '/' and appending ".properties", and using
+ * ClassLoader.getResource(). If a file is found, then a
+ * PropertyResourceBundle is created from the file's contents.</li>
+ * </ul>
+ * If no resource bundle was found, a MissingResourceException is thrown.
+ *
+ * <p>Next, the parent chain is implemented. The remaining candidate names
+ * in the above sequence are tested in a similar manner, and if any results
+ * in a resource bundle, it is assigned as the parent of the first bundle
+ * using the <code>setParent</code> method (unless the first bundle already
+ * has a parent).
+ *
+ * <p>For example, suppose the following class and property files are
+ * provided: MyResources.class, MyResources_fr_CH.properties,
+ * MyResources_fr_CH.class, MyResources_fr.properties,
+ * MyResources_en.properties, and MyResources_es_ES.class. The contents of
+ * all files are valid (that is, public non-abstract subclasses of
+ * ResourceBundle with public nullary constructors for the ".class" files,
+ * syntactically correct ".properties" files). The default locale is
+ * Locale("en", "UK").
+ *
+ * <p>Calling getBundle with the shown locale argument values instantiates
+ * resource bundles from the following sources:<ul>
+ * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent
+ * MyResources_fr.properties, parent MyResources.class</li>
+ * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("de", "DE"): result MyResources_en.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("en", "US"): result MyResources_en.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent
+ * MyResources.class</li>
+ * </ul>
+ * The file MyResources_fr_CH.properties is never used because it is hidden
+ * by MyResources_fr_CH.class.
+ *
+ * @param baseName the name of the ResourceBundle
+ * @param locale A locale
+ * @param classloader a ClassLoader
+ * @return the desired resource bundle
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if any argument is null
+ * @since 1.2
+ */
+ // This method is synchronized so that the cache is properly
+ // handled.
+ public static final synchronized ResourceBundle getBundle
+ (String baseName, Locale locale, ClassLoader classLoader)
+ {
+ // This implementation searches the bundle in the reverse direction
+ // and builds the parent chain on the fly.
+ Locale defaultLocale = Locale.getDefault();
+ if (defaultLocale != lastDefaultLocale)
+ {
+ resourceBundleCache = new HashMap();
+ lastDefaultLocale = defaultLocale;
+ }
+ HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
+ StringBuffer sb = new StringBuffer(60);
+ sb.append(baseName).append('_').append(locale);
+ String name = sb.toString();
+
+ if (cache == null)
+ {
+ cache = new HashMap();
+ resourceBundleCache.put(classLoader, cache);
+ }
+ else if (cache.containsKey(name))
+ {
+ Reference ref = (Reference) cache.get(name);
+ ResourceBundle result = null;
+ // If REF is null, that means that we added a `null' value to
+ // the hash map. That means we failed to find the bundle
+ // previously, and we cached that fact. The JDK does this, so
+ // it must be ok.
+ if (ref == null)
+ throw new MissingResourceException("Bundle " + baseName
+ + " not found",
+ baseName, "");
+ else
+ {
+ ResourceBundle rb = (ResourceBundle) ref.get();
+ if (rb != null)
+ {
+ // RB should already have the right parent, except if
+ // something very strange happened.
+ return rb;
+ }
+ // If RB is null, then we previously found it but it was
+ // collected. So we try again.
+ }
+ }
+
+ // It is ok if this returns null. We aren't required to have the
+ // base bundle.
+ ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,
+ classLoader, null, cache);
+
+ // Now use our locale, followed by the default locale. We only
+ // need to try the default locale if our locale is different, and
+ // if our locale failed to yield a result other than the base
+ // bundle.
+ ResourceBundle bundle = tryLocalBundle(baseName, locale,
+ classLoader, baseBundle, cache);
+ if (bundle == baseBundle && !locale.equals(defaultLocale))
+ {
+ bundle = tryLocalBundle(baseName, defaultLocale,
+ classLoader, baseBundle, cache);
+ // We need to record that the argument locale maps to the
+ // bundle we just found. If we didn't find a bundle, record
+ // that instead.
+ if (bundle == null)
+ cache.put(name, null);
+ else
+ cache.put(name, new SoftReference(bundle));
+ }
+
+ if (bundle == null)
+ throw new MissingResourceException("Bundle " + baseName + " not found",
+ baseName, "");
+
+ return bundle;
+ }
/**
- * The `empty' locale is created once in order to optimize
- * tryBundle().
+ * Override this method to provide the resource for a keys. This gets
+ * called by <code>getObject</code>. If you don't have a resource
+ * for the given key, you should return null instead throwing a
+ * MissingResourceException. You don't have to ask the parent, getObject()
+ * already does this; nor should you throw a MissingResourceException.
+ *
+ * @param key the key of the resource
+ * @return the resource for the key, or null if not in bundle
+ * @throws NullPointerException if key is null
+ */
+ protected abstract Object handleGetObject(String key);
+
+ /**
+ * This method should return all keys for which a resource exists; you
+ * should include the enumeration of any parent's keys, after filtering out
+ * duplicates.
+ *
+ * @return an enumeration of the keys
*/
- private static final Locale emptyLocale = new Locale ("", "");
+ public abstract Enumeration getKeys();
/**
* Tries to load a class or a property file with the specified name.
- * @param localizedName the name.
- * @param locale the locale, that must be used exactly.
- * @param classloader the classloader.
- * @param bundle the back up (parent) bundle
- * @return the resource bundle if that could be loaded, otherwise
- * <code>bundle</code>.
+ *
+ * @param localizedName the name
+ * @param locale the locale, that must be used exactly
+ * @param classloader the classloader
+ * @param bundle the backup (parent) bundle
+ * @return the resource bundle if it was loaded, otherwise the backup
*/
private static final ResourceBundle tryBundle(String localizedName,
- Locale locale,
- ClassLoader classloader,
- ResourceBundle bundle,
- HashMap cache)
+ Locale locale,
+ ClassLoader classloader,
+ ResourceBundle bundle,
+ HashMap cache)
{
- {
- // First look into the cache.
- // XXX We should remove cleared references from the cache.
- Reference ref = (Reference) cache.get(localizedName);
- if (ref != null)
- {
- ResourceBundle rb = (ResourceBundle) ref.get();
- if (rb != null)
- // rb should already have the right parent, except if
- // something very strange happened.
- return rb;
- }
- }
-
- // foundBundle holds exact matches for the localizedName resource
- // bundle, which may later be cached.
- ResourceBundle foundBundle = null;
-
- try
+ // First look into the cache.
+ if (cache.containsKey(localizedName))
{
- java.io.InputStream is;
- final String resourceName =
- localizedName.replace('.', '/') + ".properties";
- if (classloader == null)
- is = ClassLoader.getSystemResourceAsStream (resourceName);
+ Reference ref = (Reference) cache.get(localizedName);
+ ResourceBundle result = null;
+ // If REF is null, that means that we added a `null' value to
+ // the hash map. That means we failed to find the bundle
+ // previously, and we cached that fact. The JDK does this, so
+ // it must be ok.
+ if (ref == null)
+ return null;
else
- is = classloader.getResourceAsStream (resourceName);
- if (is != null)
{
- foundBundle = new PropertyResourceBundle(is);
- foundBundle.parent = bundle;
- foundBundle.locale = locale;
+ ResourceBundle rb = (ResourceBundle) ref.get();
+ if (rb != null)
+ {
+ // RB should already have the right parent, except if
+ // something very strange happened.
+ return rb;
+ }
+ // If RB is null, then we previously found it but it was
+ // collected. So we try again.
}
}
- catch (java.io.IOException ex)
- {
- }
+ // foundBundle holds exact matches for the localizedName resource
+ // bundle, which may later be cached.
+ ResourceBundle foundBundle = null;
try
{
- Class rbClass;
- if (classloader == null)
- rbClass = Class.forName(localizedName);
- else
- rbClass = classloader.loadClass(localizedName);
- foundBundle = (ResourceBundle) rbClass.newInstance();
- foundBundle.parent = bundle;
- foundBundle.locale = locale;
- }
- catch (ClassNotFoundException ex)
- {
+ Class rbClass;
+ if (classloader == null)
+ rbClass = Class.forName(localizedName);
+ else
+ rbClass = classloader.loadClass(localizedName);
+ foundBundle = (ResourceBundle) rbClass.newInstance();
+ foundBundle.parent = bundle;
+ foundBundle.locale = locale;
}
- catch (IllegalAccessException ex)
+ catch (Exception ex)
{
+ // ignore them all
+ foundBundle = null;
}
- catch (InstantiationException ex)
+ if (foundBundle == null)
{
- // ignore them all
- // XXX should we also ignore ClassCastException?
+ try
+ {
+ InputStream is;
+ final String resourceName
+ = localizedName.replace('.', '/') + ".properties";
+ if (classloader == null)
+ is = ClassLoader.getSystemResourceAsStream(resourceName);
+ else
+ is = classloader.getResourceAsStream(resourceName);
+ if (is != null)
+ {
+ foundBundle = new PropertyResourceBundle(is);
+ foundBundle.parent = bundle;
+ foundBundle.locale = locale;
+ }
+ }
+ catch (IOException ex)
+ {
+ }
}
- if (foundBundle != null)
+ // Put the result into the hash table. If we didn't find anything
+ // here, we record our parent bundle. If we record `null' that means
+ // nothing, not even the base, was found.
+ if (foundBundle == null)
+ foundBundle = bundle;
+ if (foundBundle == null)
+ cache.put(localizedName, null);
+ else
cache.put(localizedName, new SoftReference(foundBundle));
-
- return foundBundle != null ? foundBundle : bundle;
+ return foundBundle;
}
/**
* Tries to load a the bundle for a given locale, also loads the backup
* locales with the same language.
*
- * @param name the name.
- * @param locale the locale, that must be used exactly.
- * @param classloader the classloader.
- * @param bundle the back up (parent) bundle
- * @return the resource bundle if that could be loaded, otherwise
- * <code>bundle</code>.
+ * @param name the name
+ * @param locale the locale
+ * @param classloader the classloader
+ * @param bundle the backup (parent) bundle
+ * @return the resource bundle if it was loaded, otherwise the backup
*/
private static final ResourceBundle tryLocalBundle(String baseName,
Locale locale,
- ClassLoader classloader,
- ResourceBundle bundle,
- HashMap cache)
+ ClassLoader classloader,
+ ResourceBundle bundle,
+ HashMap cache)
{
final String language = locale.getLanguage();
+ final String country = locale.getCountry();
+ final String variant = locale.getVariant();
+
+ StringBuffer sb = new StringBuffer(60);
+ sb.append(baseName);
+ sb.append('_');
if (language.length() > 0)
{
- final String country = locale.getCountry();
- String name = baseName + "_" + language;
-
- if (country.length() != 0)
- {
- bundle = tryBundle(name,
- new Locale(language, ""),
- classloader, bundle, cache);
-
- name += "_" + country;
-
- final String variant = locale.getVariant();
-
- if (variant.length() != 0)
- {
- bundle = tryBundle(name,
- new Locale(language,
- country),
- classloader, bundle, cache);
-
- name += "_" + variant;
- }
- }
- bundle = tryBundle(name, locale, classloader, bundle, cache);
+ sb.append(language);
+ bundle = tryBundle(sb.toString(), new Locale(language),
+ classloader, bundle, cache);
}
- return bundle;
- }
+ // If LANGUAGE was empty, we still need to try the other
+ // components, and the `_' is required.
+ sb.append('_');
- /**
- * Get the appropriate ResourceBundle for the given locale.
- * @param baseName the name of the ResourceBundle. This should be
- * a name of a Class or a properties file. See the class
- * description for details.
- * @param locale A locale.
- * @param classloader a ClassLoader.
- * @return the desired resource bundle
- * @exception MissingResourceException
- * if the resource bundle couldn't be found.
- */
- // This method is synchronized so that the cache is properly
- // handled.
- public static final synchronized ResourceBundle getBundle(String baseName,
- Locale locale,
- ClassLoader classLoader)
- throws MissingResourceException
- {
- // This implementation searches the bundle in the reverse direction
- // and builds the parent chain on the fly.
-
- HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
- if (cache == null)
+ if (country.length() > 0)
{
- cache = new HashMap();
- resourceBundleCache.put(classLoader, cache);
- }
- else
- {
- // Fast path: If baseName + "_" + locale is in cache use it.
- String name = baseName + "_" + locale.toString();
- Reference ref = (Reference) cache.get(name);
- if (ref != null)
- {
- ResourceBundle rb = (ResourceBundle) ref.get();
- if (rb != null)
- // rb should already have the right parent, except if
- // something very strange happened.
- return rb;
- }
+ sb.append(country);
+ bundle = tryBundle(sb.toString(), new Locale(language, country),
+ classloader, bundle, cache);
}
+ sb.append('_');
- ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,
- classLoader, null, cache);
- if (baseBundle == null)
- // JDK says, that if one provides a bundle base_en_UK, one
- // must also provide the bundles base_en and base.
- // This implies that if there is no bundle for base, there
- // is no bundle at all.
- throw new MissingResourceException("Bundle " + baseName + " not found", baseName, "");
-
- // Now use the default locale.
- ResourceBundle bundle = tryLocalBundle(baseName, locale,
- classLoader, baseBundle, cache);
- if (bundle == baseBundle && !locale.equals(Locale.getDefault()))
+ if (variant.length() > 0)
{
- bundle = tryLocalBundle(baseName, Locale.getDefault(),
- classLoader, baseBundle, cache);
+ sb.append(variant);
+ bundle = tryBundle(sb.toString(), locale,
+ classloader, bundle, cache);
}
- return bundle;
- }
- /**
- * Return the actual locale of this bundle. You can use it after
- * calling getBundle, to know if the bundle for the desired locale
- * was loaded or if the fall back was used.
- */
- public Locale getLocale()
- {
- return locale;
- }
-
- /**
- * Set the parent of this bundle. This is consulted when you call
- * getObject and there is no such resource in the current bundle.
- * @param parent the parent of this bundle.
- */
- protected void setParent(ResourceBundle parent)
- {
- // Shall we ignore the old parent?
- this.parent = parent;
+ return bundle;
}
-
- /**
- * Override this method to provide the resource for a keys. This gets
- * called by <code>getObject</code>. If you don't have a resource
- * for the given key, you should return null instead throwing a
- * MissingResourceException. You don't have to ask the parent,
- * getObject() already does this.
- *
- * @param key The key of the resource.
- * @return The resource for the key, or null if not in bundle.
- * @exception MissingResourceException
- * you shouldn't throw this.
- */
- protected abstract Object handleGetObject(String key)
- throws MissingResourceException;
-
- /**
- * This method should return all keys for which a resource exists.
- * @return An enumeration of the keys.
- */
- public abstract Enumeration getKeys();
}