aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/util/ResourceBundle.java
diff options
context:
space:
mode:
authorTom Tromey <tromey@redhat.com>2001-08-31 21:31:20 +0000
committerTom Tromey <tromey@gcc.gnu.org>2001-08-31 21:31:20 +0000
commit7a95ae6b51b590a8b8b9ee06768204c5d6a4a90f (patch)
tree57cf10eba5f71e6072958cb8ef14beb8842f8b91 /libjava/java/util/ResourceBundle.java
parentfb9282f91f0bc4f90f30cd6fa5a18c91f0e7e32a (diff)
downloadgcc-7a95ae6b51b590a8b8b9ee06768204c5d6a4a90f.zip
gcc-7a95ae6b51b590a8b8b9ee06768204c5d6a4a90f.tar.gz
gcc-7a95ae6b51b590a8b8b9ee06768204c5d6a4a90f.tar.bz2
Makefile.in: Rebuilt.
* Makefile.in: Rebuilt. * Makefile.am (ordinary_java_source_files): Removed EnumerationChain, added DoubleEnumeration. (nat_source_files): Added natResourceBundle.cc. * java/util/natResourceBundle.cc: New file. * gnu/java/util/DoubleEnumeration.java: New file. * gnu/gcj/util/EnumerationChain.java: Removed. * java/beans/VetoableChangeSupport.java: Merged with Classpath. * java/util/ResourceBundle.java: Merged with Classpath. * java/util/StringTokenizer.java: Merged with Classpath. * java/util/Locale.java: Merged with Classpath. * java/util/Random.java: Merged with Classpath. * java/util/PropertyResourceBundle.java: Merged with Classpath. * java/util/ListResourceBundle.java: Merged with Classpath. * java/util/ConcurrentModificationException.java: Re-merged with Classpath. * java/util/EmptyStackException.java: Likewise. * java/util/MissingResourceException.java: Likewise. * java/util/NoSuchElementException.java: Likewise. * java/util/TooManyListenersException.java: Likewise. From-SVN: r45335
Diffstat (limited to 'libjava/java/util/ResourceBundle.java')
-rw-r--r--libjava/java/util/ResourceBundle.java574
1 files changed, 391 insertions, 183 deletions
diff --git a/libjava/java/util/ResourceBundle.java b/libjava/java/util/ResourceBundle.java
index 61738c9..c0ff673 100644
--- a/libjava/java/util/ResourceBundle.java
+++ b/libjava/java/util/ResourceBundle.java
@@ -1,223 +1,431 @@
-/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
+/* java.util.ResourceBundle
+ Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
- This file is part of libgcj.
+This file is part of GNU Classpath.
-This software is copyrighted work licensed under the terms of the
-Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
-details. */
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
-package java.util;
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
-import java.io.InputStream;
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
-/**
- * @author Anthony Green <green@cygnus.com>
- * @date November 26, 1998.
- */
+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. */
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3,
- * and "The Java Language Specification", ISBN 0-201-63451-1. */
+package java.util;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+
+/**
+ * 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
+ * <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
+ * <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.)
+ *
+ * @see Locale
+ * @see PropertyResourceBundle
+ * @author Jochen Hoenicke */
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.
+ */
protected ResourceBundle parent;
- // This is used to cache resource bundles.
- private static Hashtable resource_cache = new Hashtable ();
-
- public ResourceBundle ()
- {
- }
-
- public Locale getLocale()
+ /**
+ * The locale of this resource bundle. You can read this with
+ * <code>getLocale</code> and it is automatically set in
+ * <code>getBundle</code>.
+ */
+ private Locale locale;
+
+ /**
+ * The constructor. It does nothing special.
+ */
+ public ResourceBundle()
{
- // FIXME: Stub added for this missing method because it is needed for AWT.
- return null;
}
- public final String getString (String key) throws MissingResourceException
- {
- return (String) getObject(key);
- }
+ /**
+ * 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.
+ */
+ public final String getString(String key) throws MissingResourceException
+ {
+ return (String) getObject(key);
+ }
- public final String[] getStringArray (String key)
+ /**
+ * 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.
+ */
+ public final String[] getStringArray(String key)
throws MissingResourceException
- {
- return (String[]) getObject(key);
- }
+ {
+ 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.
+ */
public final Object getObject(String key) throws MissingResourceException
- {
- Object result;
+ {
+ 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
- {
- return handleGetObject (key);
- }
- catch (MissingResourceException ex)
- {
- if (parent != null)
- return parent.getObject(key);
- else
- throw ex;
- }
- }
+ /**
+ * 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).
+ *
+ * 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 an array containing the classes for the callers.
+ */
+ private static native Class[] getClassContext();
+
+ /**
+ * 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.
+ * @return the desired resource bundle
+ * @exception MissingResourceException
+ * if the resource bundle couldn't be found. */
+ public static final ResourceBundle getBundle(String baseName)
+ throws MissingResourceException
+ {
+ return getBundle(baseName, Locale.getDefault(),
+ getClassContext()[1].getClassLoader());
+ }
- public static final ResourceBundle getBundle(String baseName)
+ /**
+ * 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.
+ * @return the desired resource bundle
+ * @exception MissingResourceException
+ * if the resource bundle couldn't be found.
+ */
+ public static final ResourceBundle getBundle(String baseName,
+ Locale locale)
throws MissingResourceException
- {
- return getBundle(baseName, Locale.getDefault());
- }
+ {
+ return getBundle(baseName, locale, getClassContext()[1].getClassLoader());
+ }
- // Start searching with the name bundleName. Continue searching by
- // stripping off the '_' delimited tails until the search name is
- // the same as stopHere.
- private static final ResourceBundle trySomeGetBundle (String bundleName,
- String stopHere,
- ClassLoader loader)
+ /**
+ * 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();
+
+ /**
+ * Tries to load a class or a property file with the specified name.
+ * @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>.
+ */
+ private static final ResourceBundle tryBundle(String localizedName,
+ Locale locale,
+ ClassLoader classloader,
+ ResourceBundle bundle,
+ HashMap cache)
+ {
{
- Class rbc;
- ResourceBundle needs_parent = null, r, result = null;
-
- while (true)
+ // First look into the cache.
+ // XXX We should remove cleared references from the cache.
+ Reference ref = (Reference) cache.get(localizedName);
+ if (ref != null)
{
- try
- {
- rbc = Class.forName(bundleName, true, loader);
- r = null;
- try
- {
- r = (ResourceBundle) rbc.newInstance();
- }
- catch (IllegalAccessException ex)
- {
- // Fall through
- }
- catch (InstantiationException ex)
- {
- // Fall through
- }
- if (r != null)
- {
- if (result == null)
- result = r;
- if (needs_parent != null)
- {
- // We've been through the loop one or more times
- // already. Set the parent and keep going.
- needs_parent.setParent(r);
- }
- needs_parent = r;
- }
- }
- catch (ClassNotFoundException ex)
- {
- // Fall through.
- }
-
- // Look for a properties file.
- InputStream i = loader.getResourceAsStream (bundleName.replace ('.',
- '/')
- + ".properties");
- if (i != null)
- {
- try
- {
- return new PropertyResourceBundle (i);
- }
- catch (java.io.IOException e)
- {
- // The docs don't appear to define what happens in
- // this case, but it seems like continuing the
- // search is a reasonable thing to do.
- }
- }
-
- if (bundleName.equals(stopHere))
- return result;
- else
- {
- int last = bundleName.lastIndexOf('_');
-
- // No more underscores?
- if (last == -1)
- return result;
-
- // Loop around, testing this new shorter name.
- bundleName = bundleName.substring(0, last);
- }
+ ResourceBundle rb = (ResourceBundle) ref.get();
+ if (rb != null)
+ // rb should already have the right parent, except if
+ // something very strange happened.
+ return rb;
}
}
- // Search for bundles, but stop at baseName_language (if required).
- // This is synchronized so that the cache works correctly.
- private static final synchronized ResourceBundle
- partialGetBundle (String baseName, Locale locale, boolean langStop,
- ClassLoader loader)
- {
- ResourceBundle rb;
-
- // Explicitly invoke locale.toString() to force a
- // NullPointerException when required.
- String bundleName = baseName + "_" + locale.toString();
-
- // Check the cache.
- Object obj = resource_cache.get(bundleName);
- if (obj != null)
- return (ResourceBundle) obj;
-
- String stopHere = (baseName
- + (langStop ? ("_" + locale.getLanguage()) : ""));
-
-
- rb = trySomeGetBundle(bundleName, stopHere, loader);
- if (rb != null)
- resource_cache.put(bundleName, rb);
-
- return rb;
- }
+ try
+ {
+ java.io.InputStream is;
+ if (classloader == null)
+ is = ClassLoader.getSystemResourceAsStream
+ (localizedName.replace('.', '/') + ".properties");
+ else
+ is = classloader.getResourceAsStream
+ (localizedName.replace('.', '/') + ".properties");
+ if (is != null)
+ {
+ ResourceBundle rb = new PropertyResourceBundle(is);
+ rb.parent = bundle;
+ rb.locale = locale;
+ bundle = rb;
+ }
+ }
+ catch (java.io.IOException ex)
+ {
+ }
+
+ try
+ {
+ Class rbClass;
+ if (classloader == null)
+ rbClass = Class.forName(localizedName);
+ else
+ rbClass = classloader.loadClass(localizedName);
+ ResourceBundle rb = (ResourceBundle) rbClass.newInstance();
+ rb.parent = bundle;
+ rb.locale = locale;
+ bundle = rb;
+ }
+ catch (ClassNotFoundException ex)
+ {
+ }
+ catch (IllegalAccessException ex)
+ {
+ }
+ catch (InstantiationException ex)
+ {
+ // ignore them all
+ // XXX should we also ignore ClassCastException?
+ }
+
+ // Put the bundle in the cache
+ if (bundle != null)
+ cache.put(localizedName, new SoftReference(bundle));
+
+ return bundle;
+ }
- public static final ResourceBundle getBundle (String baseName,
- Locale locale)
+ /**
+ * 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>.
+ */
+ private static final ResourceBundle tryLocalBundle(String baseName,
+ Locale locale,
+ ClassLoader classloader,
+ ResourceBundle bundle,
+ HashMap cache)
{
- return getBundle (baseName, locale, ClassLoader.getSystemClassLoader ());
+ if (locale.getLanguage().length() > 0)
+ {
+ String name = baseName + "_" + locale.getLanguage();
+
+ if (locale.getCountry().length() != 0)
+ {
+ bundle = tryBundle(name,
+ new Locale(locale.getLanguage(), ""),
+ classloader, bundle, cache);
+
+ name += "_" + locale.getCountry();
+
+ if (locale.getVariant().length() != 0)
+ {
+ bundle = tryBundle(name,
+ new Locale(locale.getLanguage(),
+ locale.getCountry()),
+ classloader, bundle, cache);
+
+ name += "_" + locale.getVariant();
+ }
+ }
+ bundle = tryBundle(name, locale, classloader, bundle, cache);
+ }
+ return bundle;
}
- public static final ResourceBundle getBundle (String baseName,
- Locale locale,
- ClassLoader loader)
+ /**
+ * 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
- {
- ResourceBundle rb;
- Class rbc;
-
- if (baseName == null)
- throw new NullPointerException ();
-
- rb = partialGetBundle(baseName, locale, false, loader);
- if (rb != null)
- return rb;
-
- // Finally, try the default locale.
- if (! locale.equals(Locale.getDefault()))
- {
- rb = partialGetBundle(baseName, Locale.getDefault(), true, loader);
- if (rb != null)
- return rb;
- }
+ {
+ // 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)
+ {
+ 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;
+ }
+ }
+
+ ResourceBundle baseBundle = tryBundle(baseName, new Locale("", ""),
+ 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 not found", baseName, "");
+
+ // Now use the default locale.
+ ResourceBundle bundle = tryLocalBundle(baseName, locale,
+ classLoader, baseBundle, cache);
+ if (bundle == baseBundle && !locale.equals(Locale.getDefault()))
+ {
+ bundle = tryLocalBundle(baseName, Locale.getDefault(),
+ classLoader, baseBundle, cache);
+ }
+ return bundle;
+ }
- throw new MissingResourceException("can't load bundle",
- baseName,
- "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)
- {
- this.parent = parent;
- }
+ {
+ // Shall we ignore the old parent?
+ this.parent = parent;
+ }
- protected abstract Object handleGetObject(String key)
+ /**
+ * 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();
}