diff options
author | Tom Tromey <tromey@redhat.com> | 2001-08-31 21:31:20 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2001-08-31 21:31:20 +0000 |
commit | 7a95ae6b51b590a8b8b9ee06768204c5d6a4a90f (patch) | |
tree | 57cf10eba5f71e6072958cb8ef14beb8842f8b91 /libjava/java/util/ResourceBundle.java | |
parent | fb9282f91f0bc4f90f30cd6fa5a18c91f0e7e32a (diff) | |
download | gcc-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.java | 574 |
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(); } |