Commit 21f56031 by Bryce McKinlay Committed by Bryce McKinlay

ResourceBundle.java (bundleCache): Renamed from resourceBundleCache.

2004-07-09  Bryce McKinlay  <mckinlay@redhat.com>

	* java/util/ResourceBundle.java (bundleCache): Renamed from
	resourceBundleCache. Update comments.
	(getObject): Don't catch MissingResourceException.
	(getBundle(String)): Remove 'final'. Use system classloader if
	getCallingClassLoader returned null.
	(getBundle(String, Locale)): Likewise.
	(BundleKey): New private class. HashMap key for bundle cache lookup.
	(lookupKey): New. Singleton instance of BundleKey.
	(nullEntry): New. Cache entry to represent failed lookups.
	(getBundle(String, Locale, ClassLoader)): Re-written to use new
	caching strategy, no-allocation lookup, and new tryBundle methods.
	(tryBundle(String, ClassLoader)): New. Load a locale-qualified
	bundle name using given classloader.
	(tryBundle(String, Locale, ClassLoader, boolean): New. Qualify
	baseName for given Locale and attempt to load bundle.

From-SVN: r84434
parent 17a916d4
2004-07-09 Bryce McKinlay <mckinlay@redhat.com> 2004-07-09 Bryce McKinlay <mckinlay@redhat.com>
* java/util/ResourceBundle.java (bundleCache): Renamed from
resourceBundleCache. Update comments.
(getObject): Don't catch MissingResourceException.
(getBundle(String)): Remove 'final'. Use system classloader if
getCallingClassLoader returned null.
(getBundle(String, Locale)): Likewise.
(BundleKey): New private class. HashMap key for bundle cache lookup.
(lookupKey): New. Singleton instance of BundleKey.
(nullEntry): New. Cache entry to represent failed lookups.
(getBundle(String, Locale, ClassLoader)): Re-written to use new
caching strategy, no-allocation lookup, and new tryBundle methods.
(tryBundle(String, ClassLoader)): New. Load a locale-qualified bundle
name using given classloader.
(tryBundle(String, Locale, ClassLoader, boolean): New. Qualify
baseName for given Locale and attempt to load bundle.
2004-07-09 Bryce McKinlay <mckinlay@redhat.com>
* javax/swing/plaf/basic/BasicMenuUI.java (mousePressed): Remove * javax/swing/plaf/basic/BasicMenuUI.java (mousePressed): Remove
illegal protected method calls. illegal protected method calls.
......
...@@ -105,12 +105,9 @@ public abstract class ResourceBundle ...@@ -105,12 +105,9 @@ public abstract class ResourceBundle
private static native ClassLoader getCallingClassLoader(); private static native ClassLoader getCallingClassLoader();
/** /**
* The resource bundle cache. This is a two-level hash map: The key * The resource bundle cache.
* 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; private static Map bundleCache;
/** /**
* The last default Locale we saw. If this ever changes then we have to * The last default Locale we saw. If this ever changes then we have to
...@@ -172,15 +169,11 @@ public abstract class ResourceBundle ...@@ -172,15 +169,11 @@ public abstract class ResourceBundle
public final Object getObject(String key) public final Object getObject(String key)
{ {
for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent) for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
try {
{ Object o = bundle.handleGetObject(key);
Object o = bundle.handleGetObject(key); if (o != null)
if (o != null) return o;
return o; }
}
catch (MissingResourceException ex)
{
}
throw new MissingResourceException("Key not found", getClass().getName(), throw new MissingResourceException("Key not found", getClass().getName(),
key); key);
...@@ -220,10 +213,12 @@ public abstract class ResourceBundle ...@@ -220,10 +213,12 @@ public abstract class ResourceBundle
* @throws MissingResourceException if the resource bundle can't be found * @throws MissingResourceException if the resource bundle can't be found
* @throws NullPointerException if baseName is null * @throws NullPointerException if baseName is null
*/ */
public static final ResourceBundle getBundle(String baseName) public static ResourceBundle getBundle(String baseName)
{ {
return getBundle(baseName, Locale.getDefault(), ClassLoader cl = getCallingClassLoader();
getCallingClassLoader()); if (cl == null)
cl = ClassLoader.getSystemClassLoader();
return getBundle(baseName, Locale.getDefault(), cl);
} }
/** /**
...@@ -238,11 +233,74 @@ public abstract class ResourceBundle ...@@ -238,11 +233,74 @@ public abstract class ResourceBundle
* @throws MissingResourceException if the resource bundle can't be found * @throws MissingResourceException if the resource bundle can't be found
* @throws NullPointerException if baseName or locale is null * @throws NullPointerException if baseName or locale is null
*/ */
public static final ResourceBundle getBundle(String baseName, public static ResourceBundle getBundle(String baseName, Locale locale)
Locale locale)
{ {
return getBundle(baseName, locale, getCallingClassLoader()); ClassLoader cl = getCallingClassLoader();
if (cl == null)
cl = ClassLoader.getSystemClassLoader();
return getBundle(baseName, locale, cl);
}
/** Cache key for the ResourceBundle cache. Resource bundles are keyed
by the combination of bundle name, locale, and class loader. */
private static class BundleKey implements Cloneable
{
String baseName;
Locale locale;
ClassLoader classLoader;
int hashcode;
BundleKey() {}
BundleKey(String s, Locale l, ClassLoader cl)
{
set(s, l, cl);
}
void set(String s, Locale l, ClassLoader cl)
{
baseName = s;
locale = l;
classLoader = cl;
hashcode = baseName.hashCode() ^ locale.hashCode() ^
classLoader.hashCode();
}
public int hashCode()
{
return hashcode;
}
public boolean equals(Object o)
{
if (! (o instanceof BundleKey))
return false;
BundleKey key = (BundleKey) o;
return hashcode == key.hashcode &&
baseName.equals(key.baseName) &&
locale.equals(key.locale) &&
classLoader.equals(key.classLoader);
}
public Object clone()
{
Object clone = null;
try
{
clone = super.clone();
}
catch (CloneNotSupportedException x) {}
return clone;
}
} }
/** A cache lookup key. This avoids having to a new one for every
* getBundle() call. */
private static BundleKey lookupKey = new BundleKey();
/** Singleton cache entry to represent previous failed lookups. */
private static Object nullEntry = new Object();
/** /**
* Get the appropriate ResourceBundle for the given locale. The following * Get the appropriate ResourceBundle for the given locale. The following
...@@ -320,81 +378,59 @@ public abstract class ResourceBundle ...@@ -320,81 +378,59 @@ public abstract class ResourceBundle
*/ */
// This method is synchronized so that the cache is properly // This method is synchronized so that the cache is properly
// handled. // handled.
public static final synchronized ResourceBundle getBundle public static synchronized ResourceBundle getBundle
(String baseName, Locale locale, ClassLoader classLoader) (String baseName, Locale locale, ClassLoader classLoader)
{ {
// This implementation searches the bundle in the reverse direction // If the default locale changed since the last time we were called,
// and builds the parent chain on the fly. // all cache entries are invalidated.
Locale defaultLocale = Locale.getDefault(); Locale defaultLocale = Locale.getDefault();
if (defaultLocale != lastDefaultLocale) if (defaultLocale != lastDefaultLocale)
{ {
resourceBundleCache = new HashMap(); bundleCache = new HashMap();
lastDefaultLocale = defaultLocale; 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) // This will throw NullPointerException if any arguments are null.
lookupKey.set(baseName, locale, classLoader);
Object obj = bundleCache.get(lookupKey);
ResourceBundle rb = null;
if (obj instanceof ResourceBundle)
{ {
cache = new HashMap(); return (ResourceBundle) obj;
resourceBundleCache.put(classLoader, cache);
} }
else if (cache.containsKey(name)) else if (obj == nullEntry)
{ {
Reference ref = (Reference) cache.get(name); // Lookup has failed previously. Fall through.
// 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.
}
} }
else
// 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, // First, look for a bundle for the specified locale. We don't want
classLoader, baseBundle, cache); // the base bundle this time.
// We need to record that the argument locale maps to the boolean wantBase = locale.equals(defaultLocale);
// bundle we just found. If we didn't find a bundle, record ResourceBundle bundle = tryBundle(baseName, locale, classLoader,
// that instead. wantBase);
if (bundle == null)
cache.put(name, null); // Try the default locale if neccessary.
if (bundle == null && !locale.equals(defaultLocale))
bundle = tryBundle(baseName, defaultLocale, classLoader, true);
BundleKey key = (BundleKey) lookupKey.clone();
if (bundle == null)
{
// Cache the fact that this lookup has previously failed.
bundleCache.put(key, nullEntry);
}
else else
cache.put(name, new SoftReference(bundle)); {
// Cache the result and return it.
bundleCache.put(key, bundle);
return bundle;
}
} }
if (bundle == null) throw new MissingResourceException("Bundle " + baseName + " not found",
throw new MissingResourceException("Bundle " + baseName + " not found", baseName, "");
baseName, "");
return bundle;
} }
/** /**
...@@ -423,44 +459,13 @@ public abstract class ResourceBundle ...@@ -423,44 +459,13 @@ public abstract class ResourceBundle
* Tries to load a class or a property file with the specified name. * Tries to load a class or a property file with the specified name.
* *
* @param localizedName the name * @param localizedName the name
* @param locale the locale, that must be used exactly
* @param classloader the classloader * @param classloader the classloader
* @param bundle the backup (parent) bundle
* @return the resource bundle if it was loaded, otherwise the backup * @return the resource bundle if it was loaded, otherwise the backup
*/ */
private static final ResourceBundle tryBundle(String localizedName, private static ResourceBundle tryBundle(String localizedName,
Locale locale, ClassLoader classloader)
ClassLoader classloader,
ResourceBundle bundle,
HashMap cache)
{ {
// First look into the cache. ResourceBundle bundle = null;
if (cache.containsKey(localizedName))
{
Reference ref = (Reference) cache.get(localizedName);
// 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
{
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.
}
}
// foundBundle holds exact matches for the localizedName resource
// bundle, which may later be cached.
ResourceBundle foundBundle = null;
try try
{ {
Class rbClass; Class rbClass;
...@@ -468,48 +473,36 @@ public abstract class ResourceBundle ...@@ -468,48 +473,36 @@ public abstract class ResourceBundle
rbClass = Class.forName(localizedName); rbClass = Class.forName(localizedName);
else else
rbClass = classloader.loadClass(localizedName); rbClass = classloader.loadClass(localizedName);
foundBundle = (ResourceBundle) rbClass.newInstance(); bundle = (ResourceBundle) rbClass.newInstance();
foundBundle.parent = bundle;
foundBundle.locale = locale;
}
catch (Exception ex)
{
// ignore them all
foundBundle = null;
} }
if (foundBundle == null) catch (IllegalAccessException ex) {}
catch (InstantiationException ex) {}
catch (ClassNotFoundException ex) {}
if (bundle == null)
{ {
try try
{ {
InputStream is; InputStream is;
final String resourceName String resourceName
= localizedName.replace('.', '/') + ".properties"; = localizedName.replace('.', '/') + ".properties";
if (classloader == null) if (classloader == null)
is = ClassLoader.getSystemResourceAsStream(resourceName); is = ClassLoader.getSystemResourceAsStream(resourceName);
else else
is = classloader.getResourceAsStream(resourceName); is = classloader.getResourceAsStream(resourceName);
if (is != null) if (is != null)
{ bundle = new PropertyResourceBundle(is);
foundBundle = new PropertyResourceBundle(is);
foundBundle.parent = bundle;
foundBundle.locale = locale;
}
} }
catch (IOException ex) catch (IOException ex)
{ {
MissingResourceException mre = new MissingResourceException
("Failed to load bundle", localizedName, "");
mre.initCause(ex);
throw mre;
} }
} }
// Put the result into the hash table. If we didn't find anything return bundle;
// 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;
} }
/** /**
...@@ -520,47 +513,71 @@ public abstract class ResourceBundle ...@@ -520,47 +513,71 @@ public abstract class ResourceBundle
* @param locale the locale * @param locale the locale
* @param classloader the classloader * @param classloader the classloader
* @param bundle the backup (parent) bundle * @param bundle the backup (parent) bundle
* @param wantBase whether a resource bundle made only from the base name
* (with no locale information attached) should be returned.
* @return the resource bundle if it was loaded, otherwise the backup * @return the resource bundle if it was loaded, otherwise the backup
*/ */
private static final ResourceBundle tryLocalBundle(String baseName, private static ResourceBundle tryBundle(String baseName, Locale locale,
Locale locale, ClassLoader classLoader,
ClassLoader classloader, boolean wantBase)
ResourceBundle bundle,
HashMap cache)
{ {
final String language = locale.getLanguage(); String language = locale.getLanguage();
final String country = locale.getCountry(); String country = locale.getCountry();
final String variant = locale.getVariant(); String variant = locale.getVariant();
int baseLen = baseName.length();
StringBuffer sb = new StringBuffer(60); // Build up a StringBuffer containing the complete bundle name, fully
sb.append(baseName); // qualified by locale.
sb.append('_'); StringBuffer sb = new StringBuffer(baseLen + variant.length() + 7);
sb.append(baseName);
if (language.length() > 0) if (language.length() > 0)
{ {
sb.append('_');
sb.append(language); sb.append(language);
bundle = tryBundle(sb.toString(), new Locale(language),
classloader, bundle, cache); if (country.length() > 0)
} {
// If LANGUAGE was empty, we still need to try the other sb.append('_');
// components, and the `_' is required. sb.append(country);
sb.append('_');
if (variant.length() > 0)
if (country.length() > 0) {
{ sb.append('_');
sb.append(country); sb.append(variant);
bundle = tryBundle(sb.toString(), new Locale(language, country), }
classloader, bundle, cache); }
} }
sb.append('_');
if (variant.length() > 0) // Now try to load bundles, starting with the most specialized name.
// Build up the parent chain as we go.
String bundleName = sb.toString();
ResourceBundle first = null; // The most specialized bundle.
ResourceBundle last = null; // The least specialized bundle.
while (true)
{ {
sb.append(variant); ResourceBundle foundBundle = tryBundle(bundleName, classLoader);
bundle = tryBundle(sb.toString(), locale, if (foundBundle != null)
classloader, bundle, cache); {
if (first == null)
first = foundBundle;
if (last != null)
last.parent = foundBundle;
foundBundle.locale = locale;
last = foundBundle;
}
int idx = bundleName.lastIndexOf('_');
// Try the non-localized base name only if we already have a
// localized child bundle, or wantBase is true.
if (idx > baseLen || (idx == baseLen && (first != null || wantBase)))
bundleName = bundleName.substring(0, idx);
else
break;
} }
return bundle; return first;
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment