[kaffe] CVS kaffe (dalibor): Resynced with GNU Classpath: text and util

Kaffe CVS cvs-commits at kaffe.org
Tue Jun 15 14:14:03 PDT 2004


PatchSet 4847 
Date: 2004/06/15 18:42:51
Author: dalibor
Branch: HEAD
Tag: (none) 
Log:
Resynced with GNU Classpath: text and util

2003-06-15  Dalibor Topic  <robilad at kaffe.org>

        * libraries/javalib/java/util/ResourceBundle.java:
        Replaced by implementation from GNU Classpath.

        * libraries/javalib/java/text/BreakIterator.java,
        libraries/javalib/java/text/Collator.java,
        libraries/javalib/java/text/DateFormat.java,
        libraries/javalib/java/text/DateFormatSymbols.java,
        libraries/javalib/java/text/NumberFormat.java,
        libraries/javalib/java/util/Calendar.java,
        libraries/javalib/java/util/Currency.java,
        libraries/javalib/java/util/GregorianCalendar.java,
        libraries/javalib/java/util/SimpleTimeZone.java,
        libraries/javalib/java/util/zip/InflaterInputStream.java:
        Resynced with GNU Classpath.

        2004-06-15  Jerry Quinn  <jlquinn at optonline.net>

        * java/util/GregorianCalendar.java (computeTime):  Skip buggy formulae
        when we already know the answer.
        * java/util/SimpleTimeZone.java (serialVersionOnStream): Bump to 2.
        (setStartRule,setEndRule): Don't take abs of day number.
        (getOffset): Clarify docs.  Add argument checks.
        (isBefore): Take abs of day number in DOW_LE_DOM_MODE.
        (equals,hasSameRules,toString,readObject): Use startTimeMode and
        endTimeMode.

        2004-06-15  Bryce McKinlay  <mckinlay at redhat.com>

        * java/util/Calendar.java: Change ResourceBundle.getBundle() calls
        to pass ClassLoader argument.
        * java/util/GregorianCalendar.java: Likewise.
        * java/util/Currency.java: Likewise.
        * java/text/BreakIterator.java: Likewise.
        * java/text/Collator.java: Likewise.
        * java/text/DateFormat.java: Likewise.
        * java/text/DateFormatSymbols.java: Likewise.
        * java/text/DecimalFormatSymbols.java: Likewise.
        * java/text/NumberFormat.java: Likewise.
        * java/awt/Window.java: Likewise.

        2004-06-15  Michael Koch  <konqueror at gmx.de>

        * java/util/zip/InflaterInputStream.java
        (InflaterInputStream): Removed redundant initialization of len to 0.
        (available): Check for closed stream.
        (read): Likewise.
        (skip): Check for closed stream, simplified implementation.

Members: 
	ChangeLog:1.2414->1.2415 
	libraries/javalib/java/text/BreakIterator.java:1.9->1.10 
	libraries/javalib/java/text/Collator.java:1.14->1.15 
	libraries/javalib/java/text/DateFormat.java:1.20->1.21 
	libraries/javalib/java/text/DateFormatSymbols.java:1.12->1.13 
	libraries/javalib/java/text/NumberFormat.java:1.22->1.23 
	libraries/javalib/java/util/Calendar.java:1.20->1.21 
	libraries/javalib/java/util/Currency.java:1.3->1.4 
	libraries/javalib/java/util/GregorianCalendar.java:1.22->1.23 
	libraries/javalib/java/util/ResourceBundle.java:1.18->1.19 
	libraries/javalib/java/util/SimpleTimeZone.java:1.16->1.17 
	libraries/javalib/java/util/zip/InflaterInputStream.java:1.11->1.12 

Index: kaffe/ChangeLog
diff -u kaffe/ChangeLog:1.2414 kaffe/ChangeLog:1.2415
--- kaffe/ChangeLog:1.2414	Tue Jun 15 17:44:45 2004
+++ kaffe/ChangeLog	Tue Jun 15 18:42:51 2004
@@ -1,3 +1,53 @@
+2003-06-15  Dalibor Topic  <robilad at kaffe.org>
+
+        * libraries/javalib/java/util/ResourceBundle.java:
+	Replaced by implementation from GNU Classpath.
+	
+        * libraries/javalib/java/text/BreakIterator.java,
+        libraries/javalib/java/text/Collator.java,
+        libraries/javalib/java/text/DateFormat.java,
+        libraries/javalib/java/text/DateFormatSymbols.java,
+        libraries/javalib/java/text/NumberFormat.java,
+        libraries/javalib/java/util/Calendar.java,
+        libraries/javalib/java/util/Currency.java,
+        libraries/javalib/java/util/GregorianCalendar.java,
+        libraries/javalib/java/util/SimpleTimeZone.java,
+        libraries/javalib/java/util/zip/InflaterInputStream.java:
+	Resynced with GNU Classpath.
+
+	2004-06-15  Jerry Quinn  <jlquinn at optonline.net>
+
+        * java/util/GregorianCalendar.java (computeTime):  Skip buggy formulae
+        when we already know the answer.
+        * java/util/SimpleTimeZone.java (serialVersionOnStream): Bump to 2.
+        (setStartRule,setEndRule): Don't take abs of day number.
+        (getOffset): Clarify docs.  Add argument checks.
+        (isBefore): Take abs of day number in DOW_LE_DOM_MODE.
+        (equals,hasSameRules,toString,readObject): Use startTimeMode and
+        endTimeMode.
+
+	2004-06-15  Bryce McKinlay  <mckinlay at redhat.com>
+
+        * java/util/Calendar.java: Change ResourceBundle.getBundle() calls
+        to pass ClassLoader argument.
+        * java/util/GregorianCalendar.java: Likewise.
+        * java/util/Currency.java: Likewise.
+        * java/text/BreakIterator.java: Likewise.
+        * java/text/Collator.java: Likewise.
+        * java/text/DateFormat.java: Likewise.
+        * java/text/DateFormatSymbols.java: Likewise.
+        * java/text/DecimalFormatSymbols.java: Likewise.
+        * java/text/NumberFormat.java: Likewise.
+        * java/awt/Window.java: Likewise.
+
+	2004-06-15  Michael Koch  <konqueror at gmx.de>
+
+        * java/util/zip/InflaterInputStream.java
+        (InflaterInputStream): Removed redundant initialization of len to 0.
+        (available): Check for closed stream.
+        (read): Likewise.
+        (skip): Check for closed stream, simplified implementation.
+
 2004-06-15  Guilhem Lavaux <guilhem at kaffe.org>
 
 	* kaffe/kaffevm/baseClasses.c
Index: kaffe/libraries/javalib/java/text/BreakIterator.java
diff -u kaffe/libraries/javalib/java/text/BreakIterator.java:1.9 kaffe/libraries/javalib/java/text/BreakIterator.java:1.10
--- kaffe/libraries/javalib/java/text/BreakIterator.java:1.9	Sat Aug 16 11:03:47 2003
+++ kaffe/libraries/javalib/java/text/BreakIterator.java	Tue Jun 15 18:42:53 2004
@@ -136,7 +136,7 @@
       {
 	ResourceBundle res
 	  = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
-				     loc);
+				     loc, BreakIterator.class.getClassLoader());
 	className = res.getString(type);
       }
     catch (MissingResourceException x)
Index: kaffe/libraries/javalib/java/text/Collator.java
diff -u kaffe/libraries/javalib/java/text/Collator.java:1.14 kaffe/libraries/javalib/java/text/Collator.java:1.15
--- kaffe/libraries/javalib/java/text/Collator.java:1.14	Fri Apr 23 17:35:12 2004
+++ kaffe/libraries/javalib/java/text/Collator.java	Tue Jun 15 18:42:53 2004
@@ -303,7 +303,7 @@
     try
       {
 	res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
-				       loc);
+				       loc, Collator.class.getClassLoader());
 	pattern = res.getString("collation_rules");
       }
     catch (MissingResourceException x)
Index: kaffe/libraries/javalib/java/text/DateFormat.java
diff -u kaffe/libraries/javalib/java/text/DateFormat.java:1.20 kaffe/libraries/javalib/java/text/DateFormat.java:1.21
--- kaffe/libraries/javalib/java/text/DateFormat.java:1.20	Wed May  5 23:01:20 2004
+++ kaffe/libraries/javalib/java/text/DateFormat.java	Tue Jun 15 18:42:53 2004
@@ -325,7 +325,7 @@
     try
       {
 	res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
-				       loc);
+				       loc, DateFormat.class.getClassLoader());
       }
     catch (MissingResourceException x)
       {
Index: kaffe/libraries/javalib/java/text/DateFormatSymbols.java
diff -u kaffe/libraries/javalib/java/text/DateFormatSymbols.java:1.12 kaffe/libraries/javalib/java/text/DateFormatSymbols.java:1.13
--- kaffe/libraries/javalib/java/text/DateFormatSymbols.java:1.12	Fri Apr 23 17:35:12 2004
+++ kaffe/libraries/javalib/java/text/DateFormatSymbols.java	Tue Jun 15 18:42:53 2004
@@ -98,7 +98,8 @@
   public DateFormatSymbols (Locale locale) throws MissingResourceException
   {
     ResourceBundle res
-      = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale);
+      = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale,
+      				 getClass().getClassLoader());
 
     ampms = res.getStringArray ("ampms");
     eras = res.getStringArray ("eras");
Index: kaffe/libraries/javalib/java/text/NumberFormat.java
diff -u kaffe/libraries/javalib/java/text/NumberFormat.java:1.22 kaffe/libraries/javalib/java/text/NumberFormat.java:1.23
--- kaffe/libraries/javalib/java/text/NumberFormat.java:1.22	Fri Apr 23 17:35:12 2004
+++ kaffe/libraries/javalib/java/text/NumberFormat.java	Tue Jun 15 18:42:53 2004
@@ -310,7 +310,7 @@
     try
       {
 	res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
-				       loc);
+		loc, NumberFormat.class.getClassLoader());
       }
     catch (MissingResourceException x)
       {
Index: kaffe/libraries/javalib/java/util/Calendar.java
diff -u kaffe/libraries/javalib/java/util/Calendar.java:1.20 kaffe/libraries/javalib/java/util/Calendar.java:1.21
--- kaffe/libraries/javalib/java/util/Calendar.java:1.20	Mon May 17 13:55:06 2004
+++ kaffe/libraries/javalib/java/util/Calendar.java	Tue Jun 15 18:42:53 2004
@@ -376,7 +376,8 @@
    */
   private static ResourceBundle getBundle(Locale locale) 
   {
-    return ResourceBundle.getBundle(bundleName, locale);
+    return ResourceBundle.getBundle(bundleName, locale,
+      Calendar.class.getClassLoader());
   }
 
   /**
Index: kaffe/libraries/javalib/java/util/Currency.java
diff -u kaffe/libraries/javalib/java/util/Currency.java:1.3 kaffe/libraries/javalib/java/util/Currency.java:1.4
--- kaffe/libraries/javalib/java/util/Currency.java:1.3	Thu Apr 22 11:40:44 2004
+++ kaffe/libraries/javalib/java/util/Currency.java	Tue Jun 15 18:42:53 2004
@@ -54,7 +54,8 @@
   private Currency (Locale loc)
   {
     this.locale = loc;
-    this.res = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", locale);
+    this.res = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", 
+      locale, Currency.class.getClassLoader());
   }
 
   /**
@@ -148,7 +149,9 @@
     // First we need to implement fully LocaleInformation*.java
     try
       {
-	ResourceBundle res = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", locale);
+	ResourceBundle res = 
+	  ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", 
+				    locale, Currency.class.getClassLoader());
 
 	if (res.equals(this.res))
 	  return res.getString ("currencySymbol");
Index: kaffe/libraries/javalib/java/util/GregorianCalendar.java
diff -u kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.22 kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.23
--- kaffe/libraries/javalib/java/util/GregorianCalendar.java:1.22	Mon Dec 29 11:15:17 2003
+++ kaffe/libraries/javalib/java/util/GregorianCalendar.java	Tue Jun 15 18:42:53 2004
@@ -1,5 +1,6 @@
 /* java.util.GregorianCalendar
-   Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004
+   Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -89,7 +90,8 @@
    */
   private static ResourceBundle getBundle(Locale locale) 
   {
-    return ResourceBundle.getBundle(bundleName, locale);
+    return ResourceBundle.getBundle(bundleName, locale,
+      GregorianCalendar.class.getClassLoader());
   }
 
   /**
@@ -441,8 +443,15 @@
       ? fields[ZONE_OFFSET] : zone.getRawOffset();
 
     int dayOfYear = daysOfYear[0] + daysOfYear[1];
-    int month = (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
-    int day = (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
+    // This formula isn't right, so check for month as a quick fix.
+    // It doesn't compensate for leap years and puts day 30 in month 1
+    // instead of month 0.
+    int month = isSet[MONTH]
+	? fields[MONTH] : (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
+    // This formula isn't right, so check for day as a quick fix.  It
+    // doesn't compensate for leap years, either.
+    int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH]
+	: (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
     int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7;
     if (weekday <= 0)
       weekday += 7;
Index: kaffe/libraries/javalib/java/util/ResourceBundle.java
diff -u kaffe/libraries/javalib/java/util/ResourceBundle.java:1.18 kaffe/libraries/javalib/java/util/ResourceBundle.java:1.19
--- kaffe/libraries/javalib/java/util/ResourceBundle.java:1.18	Mon Mar 22 11:25:08 2004
+++ kaffe/libraries/javalib/java/util/ResourceBundle.java	Tue Jun 15 18:42:53 2004
@@ -1,223 +1,608 @@
-/*
- * Java core library component.
- *
- * Copyright (c) 1997, 1998
- *      Transvirtual Technologies, Inc.  All rights reserved.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file.
- */
+/* ResourceBundle -- aids in loading resource bundles
+   Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc.
 
-package java.util;
+This file is part of GNU Classpath.
 
-import java.io.IOException;
-import java.io.InputStream;
+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.
+
+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.
+
+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.
+
+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. */
 
-import kaffe.lang.ThreadStack;
 
-public abstract class ResourceBundle {
+package java.util;
 
-	protected ResourceBundle parent;
-	static Hashtable cache = new Hashtable();
-	private Locale locale;
-
-public ResourceBundle() {
-	parent = null;
-}
-
-public static final ResourceBundle getBundle(String baseName)
-		throws MissingResourceException {
-	return getBundle(baseName, Locale.getDefault(),
-			 ThreadStack.getCallersClassLoader(true));
-}
-
-public static final ResourceBundle getBundle(String baseName, Locale locale)
-		throws MissingResourceException {
-	return getBundle(baseName, locale,
-			 ThreadStack.getCallersClassLoader(true));
-}
-
-public static ResourceBundle getBundle(String baseName, Locale locale,
-		ClassLoader loader) throws MissingResourceException {
-	
-  List names = generateCandidateBundleNames(baseName, locale);
-  Iterator iter = names.iterator();
-
-  while (iter.hasNext()) {
-    String name = (String) iter.next();
-
-    ResourceBundle bundle = getFromCache(name, loader);
-    if (bundle == null) {
-      bundle = instantiate(name, loader);
-    }
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
-    if (bundle != null) {
-      putInCache(name, loader, bundle);
-      instantiateParentChain(bundle, name, loader);
-      return bundle;
+/**
+ * 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</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>.
+ *
+ * <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 at email.byu.edu)
+ * @see Locale
+ * @see ListResourceBundle
+ * @see PropertyResourceBundle
+ * @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.
+   */
+  protected ResourceBundle parent;
+
+  /**
+   * 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;
+      
+  /**
+   * We override SecurityManager in order to access getClassContext().
+   */
+  private static final class Security extends SecurityManager
+  {
+    /**
+     * Avoid accessor method of private constructor.
+     */
+    Security()
+    {
     }
-  }
-
-  throw new MissingResourceException("BaseName: " + baseName + " Locale: " + locale + " ClassLoader : " + loader, "ResourceBundle", baseName);
-}
 
+    /**
+     * 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();
+	}
 
-  private static ResourceBundle getFromCache(String name, ClassLoader loader) {
-    
-    return (ResourceBundle)cache.get (loader + name);
+      return null;
+    }
   }
 
-  private static void putInCache(String name, ClassLoader loader, ResourceBundle bundle) {
+  /** 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();
+        }
+      }
+    );
 
-    cache.put (loader + name, bundle);
+  /**
+   * 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 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
+   * @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)
+  {
+    return (String) getObject(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
+   * @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)
+  {
+    return (String[]) getObject(key);
+  }
+
+  /**
+   * 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)
+  {
+    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);
   }
 
-  private static void instantiateParentChain(ResourceBundle bundle, String name, ClassLoader loader) {
-
-    int last_underscore = name.lastIndexOf('_');
-    if (last_underscore != -1) {
-      String parent_name = name.substring(0, last_underscore);
-      ResourceBundle parent = instantiate(parent_name, loader);
-      bundle.setParent(parent);
-      if (parent != null && parent.parent == null) {
-	instantiateParentChain(parent, parent_name, loader);
+  /**
+   * 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.
+   *
+   * @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.
+   *
+   * @param parent the parent of this bundle
+   */
+  protected void setParent(ResourceBundle parent)
+  {
+    this.parent = parent;
+  }
+
+  /**
+   * 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
+   * @throws MissingResourceException if the resource bundle can't be found
+   * @throws NullPointerException if baseName is null
+   */
+  public static final ResourceBundle getBundle(String baseName)
+  {
+    return getBundle(baseName, Locale.getDefault(),
+                     security.getCallingClassLoader());
+  }
+
+  /**
+   * 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
+   * @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)
+  {
+    return getBundle(baseName, locale, security.getCallingClassLoader());
+  }
+
+  /**
+   * 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:</p>
+   *
+   * <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:</p>
+   *
+   * <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>
+   *
+   * <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>
+   *
+   * <p>Calling getBundle with the shown locale argument values instantiates
+   * resource bundles from the following sources:</p>
+   *
+   * <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>
+   * 
+   * <p>The file MyResources_fr_CH.properties is never used because it is hidden
+   * by MyResources_fr_CH.class.</p>
+   *
+   * @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;
       }
-    }
-  }
-  
-  private static ResourceBundle loadProperties(String name, ClassLoader loader) {
-    InputStream strm;
-    strm = loader.getResourceAsStream(name.replace('.', '/')
-				      + ".properties");
-    if (strm != null) {
-      try {
-	return (new PropertyResourceBundle(strm));
+    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);
       }
-      catch (IOException e) {
-	e.printStackTrace();
+    else if (cache.containsKey(name))
+      {
+	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.
+	  }
       }
-    }
-    
-    return null;
-  }
-  
-  private static ResourceBundle loadClass(String name, ClassLoader loader) {
-    try {
-      Class cls = Class.forName(name.replace('/', '.'), true, loader);
-      /* 
-       * Only call newInstance if the cast to resource bundle 
-       * will indeed succeed.
-       */
-      if (ResourceBundle.class.isAssignableFrom(cls)) {
-	return ((ResourceBundle)cls.newInstance());
-      }
-    }
-    catch (ClassNotFoundException e) {
-      // ignore
-    }
-    catch (LinkageError e) {
-      e.printStackTrace();
-    }
-    catch (IllegalAccessException e) {
-      e.printStackTrace();
-    }
-    catch (InstantiationException e) {
-      e.printStackTrace();
-    }
 
-    return null;
-  }
+    // 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));
+      }
 
-private static ResourceBundle instantiate(String name, ClassLoader loader) {
+    if (bundle == null)
+      throw new MissingResourceException("Bundle " + baseName + " not found",
+					 baseName, "");
 
-  ResourceBundle bundle = loadClass(name, loader);
-  if (bundle != null) {
     return bundle;
   }
 
-  bundle = loadProperties(name, loader);
-  return bundle;
-}
-
-private static List generateCandidateBundleNames(String baseName, Locale locale) {
-  
-  String language1 = locale.getLanguage();
-  String country1 = locale.getCountry();
-  String variant1 = locale.getVariant();
-
-  Locale default_locale = Locale.getDefault();
-
-  String language2 = default_locale.getLanguage();
-  String country2 = default_locale.getCountry();
-  String variant2 = default_locale.getVariant();
- 
-  List names = new ArrayList();
-
-  if (variant1.length() != 0) {
-    names.add(baseName + '_' + language1 + '_' + country1 + '_' + variant1);
-  }
-
-  if (country1.length() != 0) {
-    names.add(baseName + '_' + language1 + '_' + country1);
-  }
+  /**
+   * 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
+   */
+  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 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)
+  {
+    // 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.
+	  }
+      }
 
-  if (language1.length() != 0) {
-    names.add(baseName + '_' + language1);
-  }
+    // 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 (Exception ex)
+      {
+        // ignore them all
+	foundBundle = null;
+      }
+    if (foundBundle == 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)
+	  {
+	  }
+      }
 
-  if (variant2.length() != 0) {
-    names.add(baseName + '_' + language2 + '_' + country2 + '_' + variant2);
-  }
+    // 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;
+  }
+
+  /**
+   * 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
+   * @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)
+  {
+    final String language = locale.getLanguage();
+    final String country = locale.getCountry();
+    final String variant = locale.getVariant();

*** Patch too long, truncated ***




More information about the kaffe mailing list