[kaffe] CVS kaffe (robilad): Resynced with GNU Classpath: ResourceBundle updated and remerged with Classpath

Kaffe CVS cvs-commits at kaffe.org
Sat Nov 27 12:26:06 PST 2004


PatchSet 5496 
Date: 2004/11/27 20:21:42
Author: robilad
Branch: HEAD
Tag: (none) 
Log:
Resynced with GNU Classpath: ResourceBundle updated and remerged with Classpath

2004-11-27  Dalibor Topic  <robilad at kaffe.org>

        * libraries/javalib/java/util/ResourceBundle.java:
        Resynced with GNU Classpath. Reverted Helmer's changes from
        2004-07-11 as they break mauve tests for
        java.util.logging.Logger.

        2004-11-20  Tom Tromey  <tromey at redhat.com>

        * java/util/ResourceBundle.java (tryBundle): Use
        Class.isAssignableFrom rather than catching ClassCastException.

        2004-11-20  Bryce McKinlay  <mckinlay at 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.

Members: 
	ChangeLog:1.3042->1.3043 
	libraries/javalib/java/util/ResourceBundle.java:1.22->1.23 

Index: kaffe/ChangeLog
diff -u kaffe/ChangeLog:1.3042 kaffe/ChangeLog:1.3043
--- kaffe/ChangeLog:1.3042	Sat Nov 27 01:35:01 2004
+++ kaffe/ChangeLog	Sat Nov 27 20:21:42 2004
@@ -1,6 +1,36 @@
 2004-11-27  Dalibor Topic  <robilad at kaffe.org>
 
 	* libraries/javalib/java/util/ResourceBundle.java:
+	Resynced with GNU Classpath. Reverted Helmer's changes from 
+	2004-07-11 as they break mauve tests for 
+	java.util.logging.Logger.
+
+	2004-11-20  Tom Tromey  <tromey at redhat.com>
+
+        * java/util/ResourceBundle.java (tryBundle): Use
+        Class.isAssignableFrom rather than catching ClassCastException.
+
+	2004-11-20  Bryce McKinlay  <mckinlay at 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-11-27  Dalibor Topic  <robilad at kaffe.org>
+
+	* libraries/javalib/java/util/ResourceBundle.java:
 	Reindented to match GNU Classpath's version more closely.
 
 2004-11-26  Dalibor Topic  <robilad at kaffe.org>
Index: kaffe/libraries/javalib/java/util/ResourceBundle.java
diff -u kaffe/libraries/javalib/java/util/ResourceBundle.java:1.22 kaffe/libraries/javalib/java/util/ResourceBundle.java:1.23
--- kaffe/libraries/javalib/java/util/ResourceBundle.java:1.22	Sat Nov 27 01:35:05 2004
+++ kaffe/libraries/javalib/java/util/ResourceBundle.java	Sat Nov 27 20:21:45 2004
@@ -104,14 +104,53 @@
    * <code>getBundle</code>.
    */
   private Locale locale;
-      
+
+  /**
+   * We override SecurityManager in order to access getClassContext().
+   */
+  private static final class Security extends SecurityManager
+  {
+    /**
+     * Avoid accessor method of private constructor.
+     */
+    Security()
+    {
+    }
+
+    /**
+     * Return the ClassLoader of the class which called into this
+     * ResourceBundle, or null if it cannot be determined.
+     */
+    ClassLoader getCallingClassLoader()
+    {
+      Class[] stack = getClassContext();
+      for (int i = 0; i < stack.length; i++)
+       {
+	 if (stack[i] != Security.class && stack[i] != ResourceBundle.class)
+	   return stack[i].getClassLoader();
+       }
+
+      return null;
+    }
+  }
+
+  /** A security context for grabbing the correct class loader. */
+  private static final Security security
+    = (Security) AccessController.doPrivileged(new PrivilegedAction()
+      {
+	// This will always work since java.util classes have (all) system
+	// permissions.
+	public Object run()
+	{
+	  return new Security();
+	}
+      }
+    );
+
   /**
-   * 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.
+   * The resource bundle cache.
    */
-  private static Map resourceBundleCache;
+  private static Map bundleCache;
 
   /**
    * The last default Locale we saw. If this ever changes then we have to
@@ -173,18 +212,14 @@
   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)
-        {
-        }
-
+      {
+        Object o = bundle.handleGetObject(key);
+        if (o != null)
+          return o;
+      }
+ 
     throw new MissingResourceException("Key not found", getClass().getName(),
-                                       key);
+				       key);
   }
 
   /**
@@ -221,10 +256,12 @@
    * @throws MissingResourceException if the resource bundle can't be found
    * @throws NullPointerException if baseName is null
    */
-  public static final ResourceBundle getBundle(String baseName)
+  public static ResourceBundle getBundle(String baseName)
   {
-    return getBundle(baseName, Locale.getDefault(),
-                     kaffe.lang.ThreadStack.getCallersClassLoader(true));
+    ClassLoader cl = security.getCallingClassLoader();
+    if (cl == null)
+      cl = ClassLoader.getSystemClassLoader();
+    return getBundle(baseName, Locale.getDefault(), cl);
   }
 
   /**
@@ -239,11 +276,62 @@
    * @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)
+  public static ResourceBundle getBundle(String baseName, Locale locale)
   {
-    return getBundle(baseName, locale, kaffe.lang.ThreadStack.getCallersClassLoader(true));
+    ClassLoader cl = security.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
+  {
+    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);
+    }    
   }
+  
+  /** 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
@@ -307,7 +395,7 @@
    * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent
    *   MyResources.class</li>
    * </ul>
-   *
+   * 
    * <p>The file MyResources_fr_CH.properties is never used because it is hidden
    * by MyResources_fr_CH.class.</p>
    *
@@ -321,81 +409,60 @@
    */
   // This method is synchronized so that the cache is properly
   // handled.
-  public static final synchronized ResourceBundle getBundle
+  public static 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.
+    // If the default locale changed since the last time we were called,
+    // all cache entries are invalidated.
     Locale defaultLocale = Locale.getDefault();
     if (defaultLocale != lastDefaultLocale)
       {
-        resourceBundleCache = new HashMap();
-        lastDefaultLocale = defaultLocale;
+	bundleCache = 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)
+    // 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();
-        resourceBundleCache.put(classLoader, cache);
+        return (ResourceBundle) obj;
       }
-    else if (cache.containsKey(name))
+    else if (obj == nullEntry)
       {
-        Reference ref = (Reference) cache.get(name);
-        // 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.
-          }
+        // Lookup has failed previously. Fall through.
       }
-
-    // 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))
+    else
       {
-        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.
+	// First, look for a bundle for the specified locale. We don't want
+	// the base bundle this time.
+	boolean wantBase = locale.equals(defaultLocale);
+	ResourceBundle bundle = tryBundle(baseName, locale, classLoader, 
+					  wantBase);
+
+        // Try the default locale if neccessary.
+	if (bundle == null && !locale.equals(defaultLocale))
+	  bundle = tryBundle(baseName, defaultLocale, classLoader, true);
+
+	BundleKey key = new BundleKey(baseName, locale, classLoader);
         if (bundle == null)
-          cache.put(name, null);
-        else
-          cache.put(name, new SoftReference(bundle));
+	  {
+	    // Cache the fact that this lookup has previously failed.
+	    bundleCache.put(key, nullEntry);
+	  }
+	else
+	  {
+            // Cache the result and return it.
+	    bundleCache.put(key, bundle);
+	    return bundle;
+	  }
       }
 
-    if (bundle == null)
-      throw new MissingResourceException("Bundle " + baseName + " not found",
-                                         baseName, "");
-
-    return bundle;
+    throw new MissingResourceException("Bundle " + baseName + " not found",
+				       baseName, "");
   }
 
   /**
@@ -424,42 +491,13 @@
    * 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 backup (parent) bundle
    * @return the resource bundle if it was loaded, otherwise the backup
    */
-  private static ResourceBundle tryBundle(String localizedName, Locale locale,
-                                          ClassLoader classloader,
-                                          ResourceBundle bundle, HashMap cache)
+  private static ResourceBundle tryBundle(String localizedName,
+                                          ClassLoader classloader)
   {
-    // First look into the cache.
-    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;
+    ResourceBundle bundle = null;
     try
       {
         Class rbClass;
@@ -467,98 +505,118 @@
           rbClass = Class.forName(localizedName);
         else
           rbClass = classloader.loadClass(localizedName);
-        foundBundle = (ResourceBundle) rbClass.newInstance();
-        foundBundle.parent = bundle;
-        foundBundle.locale = locale;
-      }
-    catch (Exception ex)
-      {
-        // ignore them all
-        foundBundle = null;
-      }
-    if (foundBundle == null)
+	// Note that we do the check up front instead of catching
+	// ClassCastException.  The reason for this is that some crazy
+	// programs (Eclipse) have classes that do not extend
+	// ResourceBundle but that have the same name as a property
+	// bundle; in fact Eclipse relies on ResourceBundle not
+	// instantiating these classes.
+	if (ResourceBundle.class.isAssignableFrom(rbClass))
+	  bundle = (ResourceBundle) rbClass.newInstance();
+      }
+    catch (IllegalAccessException ex) {}
+    catch (InstantiationException ex) {}
+    catch (ClassNotFoundException ex) {}
+
+    if (bundle == null)
       {
-        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)
-          {
-          }
+	try
+	  {
+	    InputStream is;
+	    String resourceName
+	      = localizedName.replace('.', '/') + ".properties";
+	    if (classloader == null)
+	      is = ClassLoader.getSystemResourceAsStream(resourceName);
+	    else
+	      is = classloader.getResourceAsStream(resourceName);
+	    if (is != null)
+	      bundle = new PropertyResourceBundle(is);
+	  }
+	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
-    // 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;
+    return bundle;
   }
 
   /**
    * Tries to load a the bundle for a given locale, also loads the backup
    * locales with the same language.
    *
-   * @param name the name
+   * @param baseName the raw bundle name, without locale qualifiers
    * @param locale the locale
    * @param classloader the classloader
    * @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
    */
-  private static ResourceBundle tryLocalBundle(String baseName, Locale locale,
-                                               ClassLoader classloader,
-                                               ResourceBundle bundle,
-                                               HashMap cache)
-  {
-    final String language = locale.getLanguage();
-    final String country = locale.getCountry();
-    final String variant = locale.getVariant();
+  private static ResourceBundle tryBundle(String baseName, Locale locale,
+                                          ClassLoader classLoader, 
+					  boolean wantBase)
+  {
+    String language = locale.getLanguage();
+    String country = locale.getCountry();
+    String variant = locale.getVariant();
+    
+    int baseLen = baseName.length();
+
+    // Build up a StringBuffer containing the complete bundle name, fully
+    // qualified by locale.
+    StringBuffer sb = new StringBuffer(baseLen + variant.length() + 7);
 
-    StringBuffer sb = new StringBuffer(60);
     sb.append(baseName);
-    sb.append('_');
-
+    
     if (language.length() > 0)
       {
-        sb.append(language);
-        bundle = tryBundle(sb.toString(), new Locale(language),
-                           classloader, bundle, cache);
-      }
-    // If LANGUAGE was empty, we still need to try the other
-    // components, and the `_' is required.
-    sb.append('_');
-
-    if (country.length() > 0)
-      {
-        sb.append(country);
-        bundle = tryBundle(sb.toString(), new Locale(language, country),
-                           classloader, bundle, cache);
+	sb.append('_');
+	sb.append(language);
+	
+	if (country.length() > 0)
+	  {
+	    sb.append('_');
+	    sb.append(country);
+	    
+	    if (variant.length() > 0)
+	      {
+	        sb.append('_');
+		sb.append(variant);
+	      }
+	  }
+      }
+
+    // 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)
+      {
+        ResourceBundle foundBundle = tryBundle(bundleName, classLoader);
+	if (foundBundle != null)
+	  {
+	    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;
       }
-    sb.append('_');
-
-    if (variant.length() > 0)
-      {
-        sb.append(variant);
-        bundle = tryBundle(sb.toString(), locale,
-                           classloader, bundle, cache);
-      }
-
-    return bundle;
+    
+    return first;
   }
 }




More information about the kaffe mailing list