Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / mcr-test / src / instrumented / java / util / ResourceBundle.java
diff --git a/JMCR-Stable/mcr-test/src/instrumented/java/util/ResourceBundle.java b/JMCR-Stable/mcr-test/src/instrumented/java/util/ResourceBundle.java
new file mode 100644 (file)
index 0000000..c03b8f6
--- /dev/null
@@ -0,0 +1,1130 @@
+/*
+ * @(#)ResourceBundle.java     1.71 04/08/16
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+/*
+ * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
+ * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
+ *
+ * The original version of this source code and documentation
+ * is copyrighted and owned by Taligent, Inc., a wholly-owned
+ * subsidiary of IBM. These materials are provided under terms
+ * of a License Agreement between Taligent and Sun. This technology
+ * is protected by multiple US and International patents.
+ *
+ * This notice and attribution to Taligent may not be removed.
+ * Taligent is a registered trademark of Taligent, Inc.
+ *
+ */
+
+package instrumented.java.util;
+
+import sun.misc.SoftCache;
+
+import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Enumeration;
+import java.util.Locale;
+
+/**
+ *
+ * Resource bundles contain locale-specific objects.
+ * When your program needs a locale-specific resource,
+ * a <code>String</code> for example, your program can load it
+ * from the resource bundle that is appropriate for the
+ * current user's locale. In this way, you can write
+ * program code that is largely independent of the user's
+ * locale isolating most, if not all, of the locale-specific
+ * information in resource bundles.
+ *
+ * <p>
+ * This allows you to write programs that can:
+ * <UL type=SQUARE>
+ * <LI> be easily localized, or translated, into different languages
+ * <LI> handle multiple locales at once
+ * <LI> be easily modified later to support even more locales
+ * </UL>
+ *
+ * <P>
+ * Resource bundles belong to families whose members share a common base 
+ * name, but whose names also have additional components that identify 
+ * their locales. For example, the base name of a family of resource 
+ * bundles might be "MyResources". The family should have a default 
+ * resource bundle which simply has the same name as its family - 
+ * "MyResources" - and will be used as the bundle of last resort if a
+ * specific locale is not supported. The family can then provide as
+ * many locale-specific members as needed, for example a German one
+ * named "MyResources_de".
+ *
+ * <P>
+ * Each resource bundle in a family contains the same items, but the items have
+ * been translated for the locale represented by that resource bundle.
+ * For example, both "MyResources" and "MyResources_de" may have a
+ * <code>String</code> that's used on a button for canceling operations.
+ * In "MyResources" the <code>String</code> may contain "Cancel" and in
+ * "MyResources_de" it may contain "Abbrechen".
+ *
+ * <P>
+ * If there are different resources for different countries, you
+ * can make specializations: for example, "MyResources_de_CH" contains objects for
+ * the German language (de) in Switzerland (CH). If you want to only
+ * modify some of the resources
+ * in the specialization, you can do so.
+ *
+ * <P>
+ * When your program needs a locale-specific object, it loads
+ * the <code>ResourceBundle</code> class using the
+ * {@link #getBundle(java.lang.String, java.util.Locale) getBundle}
+ * method:
+ * <blockquote>
+ * <pre>
+ * ResourceBundle myResources =
+ *      ResourceBundle.getBundle("MyResources", currentLocale);
+ * </pre>
+ * </blockquote>
+ *
+ * <P>
+ * Resource bundles contain key/value pairs. The keys uniquely
+ * identify a locale-specific object in the bundle. Here's an
+ * example of a <code>ListResourceBundle</code> that contains
+ * two key/value pairs:
+ * <blockquote>
+ * <pre>
+ * public class MyResources extends ListResourceBundle {
+ *      public Object[][] getContents() {
+ *              return contents;
+ *      }
+ *      static final Object[][] contents = {
+ *      // LOCALIZE THIS
+ *              {"OkKey", "OK"},
+ *              {"CancelKey", "Cancel"},
+ *      // END OF MATERIAL TO LOCALIZE
+ *      };
+ * }
+ * </pre>
+ * </blockquote>
+ * Keys are always <code>String</code>s.
+ * In this example, the keys are "OkKey" and "CancelKey".
+ * In the above example, the values
+ * are also <code>String</code>s--"OK" and "Cancel"--but
+ * they don't have to be. The values can be any type of object.
+ *
+ * <P>
+ * You retrieve an object from resource bundle using the appropriate
+ * getter method. Because "OkKey" and "CancelKey"
+ * are both strings, you would use <code>getString</code> to retrieve them:
+ * <blockquote>
+ * <pre>
+ * button1 = new Button(myResources.getString("OkKey"));
+ * button2 = new Button(myResources.getString("CancelKey"));
+ * </pre>
+ * </blockquote>
+ * The getter methods all require the key as an argument and return
+ * the object if found. If the object is not found, the getter method
+ * throws a <code>MissingResourceException</code>.
+ *
+ * <P>
+ * Besides <code>getString</code>, ResourceBundle also provides
+ * a method for getting string arrays, <code>getStringArray</code>,
+ * as well as a generic <code>getObject</code> method for any other
+ * type of object. When using <code>getObject</code>, you'll
+ * have to cast the result to the appropriate type. For example:
+ * <blockquote>
+ * <pre>
+ * int[] myIntegers = (int[]) myResources.getObject("intList");
+ * </pre>
+ * </blockquote>
+ *
+ * <P>
+ * The Java 2 platform provides two subclasses of <code>ResourceBundle</code>,
+ * <code>ListResourceBundle</code> and <code>PropertyResourceBundle</code>,
+ * that provide a fairly simple way to create resources.
+ * As you saw briefly in a previous example, <code>ListResourceBundle</code>
+ * manages its resource as a List of key/value pairs.
+ * <code>PropertyResourceBundle</code> uses a properties file to manage
+ * its resources.
+ *
+ * <p>
+ * If <code>ListResourceBundle</code> or <code>PropertyResourceBundle</code>
+ * do not suit your needs, you can write your own <code>ResourceBundle</code>
+ * subclass.  Your subclasses must override two methods: <code>handleGetObject</code>
+ * and <code>getKeys()</code>.
+ *
+ * <P>
+ * The following is a very simple example of a <code>ResourceBundle</code>
+ * subclass, MyResources, that manages two resources (for a larger number of
+ * resources you would probably use a <code>Hashtable</code>).
+ * Notice that you don't need to supply a value if 
+ * a "parent-level" <code>ResourceBundle</code> handles the same
+ * key with the same value (as for the okKey below).
+ * <p><strong>Example:</strong>
+ * <blockquote>
+ * <pre>
+ * // default (English language, United States)
+ * public class MyResources extends ResourceBundle {
+ *     public Object handleGetObject(String key) {
+ *         if (key.equals("okKey")) return "Ok";
+ *         if (key.equals("cancelKey")) return "Cancel";
+ *         return null;
+ *     }
+ * }
+ *
+ * // German language
+ * public class MyResources_de extends MyResources {
+ *     public Object handleGetObject(String key) {
+ *         // don't need okKey, since parent level handles it.
+ *         if (key.equals("cancelKey")) return "Abbrechen";
+ *         return null;
+ *     }
+ * }
+ * </pre>
+ * </blockquote>
+ * You do not have to restrict yourself to using a single family of
+ * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
+ * exception messages, <code>ExceptionResources</code>
+ * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
+ * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
+ * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
+ *
+ * @see ListResourceBundle
+ * @see PropertyResourceBundle
+ * @see MissingResourceException
+ * @since JDK1.1
+ */
+abstract public class ResourceBundle {
+    /**
+     * Static key used for resource lookups. Concurrent
+     * access to this object is controlled by synchronizing cacheList,
+     * not cacheKey.  A static object is used to do cache lookups
+     * for performance reasons - the assumption being that synchronization
+     * has a lower overhead than object allocation and subsequent
+     * garbage collection.
+     */
+    private static final ResourceCacheKey cacheKey = new ResourceCacheKey();
+
+    /** initial size of the bundle cache */
+    private static final int INITIAL_CACHE_SIZE = 25;
+
+    /** capacity of cache consumed before it should grow */
+    private static final float CACHE_LOAD_FACTOR = (float)1.0;
+
+    /**
+     * Maximum length of one branch of the resource search path tree.
+     * Used in getBundle.
+     */
+    private static final int MAX_BUNDLES_SEARCHED = 3;
+
+    /**
+     * This Hashtable is used to keep multiple threads from loading the
+     * same bundle concurrently.  The table entries are (cacheKey, thread)
+     * where cacheKey is the key for the bundle that is under construction
+     * and thread is the thread that is constructing the bundle.
+     * This list is manipulated in findBundle and putBundleInCache.
+     * Synchronization of this object is done through cacheList, not on
+     * this object.
+     */
+    private static final Hashtable underConstruction = new Hashtable(MAX_BUNDLES_SEARCHED, CACHE_LOAD_FACTOR);
+
+    /** constant indicating that no resource bundle was found */
+    private static final Object NOT_FOUND = new Object();
+
+    /**
+     * The cache is a map from cache keys (with bundle base name,
+     * locale, and class loader) to either a resource bundle
+     * (if one has been found) or NOT_FOUND (if no bundle has
+     * been found).
+     * The cache is a SoftCache, allowing bundles to be
+     * removed from the cache if they are no longer
+     * needed.  This will also allow the cache keys
+     * to be reclaimed along with the ClassLoaders
+     * they reference.
+     * This variable would be better named "cache", but we keep the old
+     * name for compatibility with some workarounds for bug 4212439.
+     */
+    private static SoftCache cacheList = new SoftCache(INITIAL_CACHE_SIZE, CACHE_LOAD_FACTOR);
+
+    /**
+     * Queue for reference objects referring to class loaders.
+     */
+    private static ReferenceQueue referenceQueue = new ReferenceQueue();
+
+    /**
+     * The parent bundle of this bundle.
+     * The parent bundle is searched by {@link #getObject getObject}
+     * when this bundle does not contain a particular resource.
+     */
+    protected ResourceBundle parent = null;
+
+    /**
+     * The locale for this bundle.
+     */
+    private Locale locale = null;
+
+    /**
+     * Sole constructor.  (For invocation by subclass constructors, typically
+     * implicit.)
+     */
+    public ResourceBundle() {
+    }
+
+    /**
+     * Gets a string for the given key from this resource bundle or one of its parents.
+     * Calling this method is equivalent to calling
+     * <blockquote>
+     * <code>(String) {@link #getObject(java.lang.String) getObject}(key)</code>.
+     * </blockquote>
+     *
+     * @param key the key for the desired string
+     * @exception NullPointerException if <code>key</code> is <code>null</code>
+     * @exception MissingResourceException if no object for the given key can be found
+     * @exception ClassCastException if the object found for the given key is not a string
+     * @return the string for the given key
+     */
+    public final String getString(String key) {
+        return (String) getObject(key);
+    }
+
+    /**
+     * Gets a string array for the given key from this resource bundle or one of its parents.
+     * Calling this method is equivalent to calling
+     * <blockquote>
+     * <code>(String[]) {@link #getObject(java.lang.String) getObject}(key)</code>.
+     * </blockquote>
+     *
+     * @param key the key for the desired string array
+     * @exception NullPointerException if <code>key</code> is <code>null</code>
+     * @exception MissingResourceException if no object for the given key can be found
+     * @exception ClassCastException if the object found for the given key is not a string array
+     * @return the string array for the given key
+     */
+    public final String[] getStringArray(String key) {
+        return (String[]) getObject(key);
+    }
+
+    /**
+     * Gets an object for the given key from this resource bundle or one of its parents.
+     * This method first tries to obtain the object from this resource bundle using
+     * {@link #handleGetObject(java.lang.String) handleGetObject}.
+     * If not successful, and the parent resource bundle is not null,
+     * it calls the parent's <code>getObject</code> method.
+     * If still not successful, it throws a MissingResourceException.
+     *
+     * @param key the key for the desired object
+     * @exception NullPointerException if <code>key</code> is <code>null</code>
+     * @exception MissingResourceException if no object for the given key can be found
+     * @return the object for the given key
+     */
+    public final Object getObject(String key) {
+        Object obj = handleGetObject(key);
+        if (obj == null) {
+            if (parent != null) {
+                obj = parent.getObject(key);
+            }
+            if (obj == null)
+                throw new MissingResourceException("Can't find resource for bundle "
+                                                   +this.getClass().getName()
+                                                   +", key "+key,
+                                                   this.getClass().getName(),
+                                                   key);
+        }
+        return obj;
+    }
+
+    /**
+     * Returns the locale of this resource bundle. This method can be used after a
+     * call to getBundle() to determine whether the resource bundle returned really
+     * corresponds to the requested locale or is a fallback.
+     *
+     * @return the locale of this resource bundle
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Sets the locale for this bundle.  This is the locale that this
+     * bundle actually represents and does not depend on how the
+     * bundle was found by getBundle.  Ex. if the user was looking
+     * for fr_FR and getBundle found en_US, the bundle's locale would
+     * be en_US, NOT fr_FR
+     * @param baseName the bundle's base name
+     * @param bundleName the complete bundle name including locale
+     * extension.
+     */
+    private void setLocale(String baseName, String bundleName) {
+        if (baseName.length() == bundleName.length()) {
+            locale = new Locale("", "");
+        } else if (baseName.length() < bundleName.length()) {
+            int pos = baseName.length();
+            String temp = bundleName.substring(pos + 1);
+            pos = temp.indexOf('_');
+            if (pos == -1) {
+                locale = new Locale(temp, "", "");
+                return;
+            }
+
+            String language = temp.substring(0, pos);
+            temp = temp.substring(pos + 1);
+            pos = temp.indexOf('_');
+            if (pos == -1) {
+                locale = new Locale(language, temp, "");
+                return;
+            }
+
+            String country = temp.substring(0, pos);
+            temp = temp.substring(pos + 1);
+
+            locale = new Locale(language, country, temp);
+        } else {
+            //The base name is longer than the bundle name.  Something is very wrong
+            //with the calling code.
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /*
+     * Automatic determination of the ClassLoader to be used to load
+     * resources on behalf of the client.  N.B. The client is getLoader's
+     * caller's caller.
+     */
+    private static ClassLoader getLoader() {
+        Class[] stack = getClassContext();
+        /* Magic number 2 identifies our caller's caller */
+        Class c = stack[2];
+        ClassLoader cl = (c == null) ? null : c.getClassLoader();
+        if (cl == null) {
+            cl = ClassLoader.getSystemClassLoader();
+        }
+        return cl;
+    }
+
+    private static native Class[] getClassContext();
+
+    /**
+     * Sets the parent bundle of this bundle.
+     * The parent bundle is searched by {@link #getObject getObject}
+     * when this bundle does not contain a particular resource.
+     *
+     * @param parent this bundle's parent bundle.
+     */
+    protected void setParent( ResourceBundle parent ) {
+        this.parent = parent;
+    }
+
+     /**
+      * Key used for cached resource bundles.  The key checks
+      * the resource name, the class loader, and the default
+      * locale to determine if the resource is a match to the
+      * requested one. The loader may be null, but the
+      * searchName and the default locale must have a non-null value.
+      * Note that the default locale may change over time, and
+      * lookup should always be based on the current default
+      * locale (if at all).
+      */
+     private static final class ResourceCacheKey implements Cloneable {
+        private LoaderReference loaderRef;
+        private String searchName;
+        private Locale defaultLocale;
+        private int hashCodeCache;
+
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            try {
+                final ResourceCacheKey otherEntry = (ResourceCacheKey)other;
+                //quick check to see if they are not equal
+                if (hashCodeCache != otherEntry.hashCodeCache) {
+                    return false;
+                }
+                //are the names the same?
+                if (!searchName.equals(otherEntry.searchName)) {
+                    return false;
+                }
+                // are the default locales the same?
+                if (defaultLocale == null) {
+                    if (otherEntry.defaultLocale != null) {
+                        return false;
+                    }
+                } else {
+                    if (!defaultLocale.equals(otherEntry.defaultLocale)) {
+                        return false;
+                    }
+                }
+                //are refs (both non-null) or (both null)?
+                if (loaderRef == null) {
+                    return otherEntry.loaderRef == null;
+                } else {
+                    Object loaderRefValue = loaderRef.get();
+                    return (otherEntry.loaderRef != null)
+                            // with a null reference we can no longer find
+                            // out which class loader was referenced; so
+                            // treat it as unequal
+                            && (loaderRefValue != null)
+                            && (loaderRefValue == otherEntry.loaderRef.get());
+                }
+            } catch (NullPointerException e) {
+                return false;
+            } catch (ClassCastException e) {
+                return false;
+            }
+        }
+
+        public int hashCode() {
+            return hashCodeCache;
+        }
+
+        public Object clone() {
+            try {
+                ResourceCacheKey clone = (ResourceCacheKey) super.clone();
+                if (loaderRef != null) {
+                    clone.loaderRef = new LoaderReference(loaderRef.get(), referenceQueue, clone);
+                }
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                //this should never happen
+                throw new InternalError();
+            }
+
+        }
+
+        public void setKeyValues(ClassLoader loader, String searchName, Locale defaultLocale) {
+            this.searchName = searchName;
+            hashCodeCache = searchName.hashCode();
+            this.defaultLocale = defaultLocale;
+            if (defaultLocale != null) {
+                hashCodeCache ^= defaultLocale.hashCode();
+            }
+            if (loader == null) {
+                this.loaderRef = null;
+            } else {
+                loaderRef = new LoaderReference(loader, referenceQueue, this);
+                hashCodeCache ^= loader.hashCode();
+            }
+        }
+
+        public void clear() {
+            setKeyValues(null, "", null);
+        }
+    }
+
+    /**
+     * References to class loaders are weak references, so that they can be
+     * garbage collected when nobody else is using them. The ResourceBundle
+     * class has no reason to keep class loaders alive.
+     */
+    private static final class LoaderReference extends WeakReference {
+        private ResourceCacheKey cacheKey;
+
+        LoaderReference(Object referent, ReferenceQueue q, ResourceCacheKey key) {
+            super(referent, q);
+            cacheKey = key;
+        }
+
+        ResourceCacheKey getCacheKey() {
+            return cacheKey;
+        }
+    }
+
+
+    /**
+     * Gets a resource bundle using the specified base name, the default locale,
+     * and the caller's class loader. Calling this method is equivalent to calling
+     * <blockquote>
+     * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
+     * </blockquote>
+     * except that <code>getClassLoader()</code> is run with the security
+     * privileges of <code>ResourceBundle</code>.
+     * See {@link #getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader) getBundle}
+     * for a complete description of the search and instantiation strategy.
+     *
+     * @param baseName the base name of the resource bundle, a fully qualified class name
+     * @exception java.lang.NullPointerException
+     *     if <code>baseName</code> is <code>null</code>
+     * @exception MissingResourceException
+     *     if no resource bundle for the specified base name can be found
+     * @return a resource bundle for the given base name and the default locale
+     */
+    public static final ResourceBundle getBundle(String baseName)
+    {
+        return getBundleImpl(baseName, Locale.getDefault(),
+        /* must determine loader here, else we break stack invariant */
+        getLoader());
+    }
+
+    /**
+     * Gets a resource bundle using the specified base name and locale,
+     * and the caller's class loader. Calling this method is equivalent to calling
+     * <blockquote>
+     * <code>getBundle(baseName, locale, this.getClass().getClassLoader())</code>,
+     * </blockquote>
+     * except that <code>getClassLoader()</code> is run with the security
+     * privileges of <code>ResourceBundle</code>.
+     * See {@link #getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader) getBundle}
+     * for a complete description of the search and instantiation strategy.
+     *
+     * @param baseName the base name of the resource bundle, a fully qualified class name
+     * @param locale the locale for which a resource bundle is desired
+     * @exception java.lang.NullPointerException
+     *     if <code>baseName</code> or <code>locale</code> is <code>null</code>
+     * @exception MissingResourceException
+     *     if no resource bundle for the specified base name can be found
+     * @return a resource bundle for the given base name and locale
+     */
+    public static final ResourceBundle getBundle(String baseName,
+                                                 Locale locale)
+    {
+        return getBundleImpl(baseName, locale, getLoader());
+    }
+
+    /**
+     * Gets a resource bundle using the specified base name, locale, and class loader.
+     *
+     * <p>
+     * Conceptually, <code>getBundle</code> uses the following strategy for locating and instantiating
+     * resource bundles:
+     * <p>
+     * <code>getBundle</code> uses the base name, the specified locale, and the default
+     * locale (obtained from {@link java.util.Locale#getDefault() Locale.getDefault})
+     * to generate a sequence of <em>candidate bundle names</em>.
+     * If the specified locale's language, country, and variant are all empty
+     * strings, then the base name is the only candidate bundle name.
+     * Otherwise, the following sequence is generated from the attribute
+     * values of the specified locale (language1, country1, and variant1)
+     * and of the default locale (language2, country2, and variant2):
+     * <ul>
+     * <li> baseName + "_" + language1 + "_" + country1 + "_" + variant1
+     * <li> baseName + "_" + language1 + "_" + country1
+     * <li> baseName + "_" + language1
+     * <li> baseName + "_" + language2 + "_" + country2 + "_" + variant2
+     * <li> baseName + "_" + language2 + "_" + country2
+     * <li> baseName + "_" + language2
+     * <li> baseName
+     * </ul>
+     * <p>
+     * Candidate bundle names where the final component is an empty string are omitted.
+     * For example, if country1 is an empty string, the second candidate bundle name is omitted.
+     *
+     * <p>
+     * <code>getBundle</code> then iterates over the candidate bundle names to find the first
+     * one for which it can <em>instantiate</em> an actual resource bundle. For each candidate
+     * bundle name, it attempts to create a resource bundle:
+     * <ul>
+     * <li>
+     * First, it attempts to load a class using the candidate bundle name.
+     * If such a class can be found and loaded using the specified class loader, is assignment
+     * compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated,
+     * <code>getBundle</code> creates a new instance of this class and uses it as the <em>result
+     * resource bundle</em>.
+     * <li>
+     * Otherwise, <code>getBundle</code> attempts to locate a property resource file.
+     * It generates a path name from the candidate bundle name by replacing all "." characters
+     * with "/" and appending the string ".properties".
+     * It attempts to find a "resource" with this name using
+     * {@link java.lang.ClassLoader#getResource(java.lang.String) ClassLoader.getResource}.
+     * (Note that a "resource" in the sense of <code>getResource</code> has nothing to do with
+     * the contents of a resource bundle, it is just a container of data, such as a file.)
+     * If it finds a "resource", it attempts to create a new
+     * {@link PropertyResourceBundle} instance from its contents.
+     * If successful, this instance becomes the <em>result resource bundle</em>.
+     * </ul>
+     *
+     * <p>
+     * If no result resource bundle has been found, a <code>MissingResourceException</code>
+     * is thrown.
+     *
+     * <p>
+     * Once a result resource bundle has been found, its parent chain is instantiated.
+     * <code>getBundle</code> iterates over the candidate bundle names that can be
+     * obtained by successively removing variant, country, and language
+     * (each time with the preceding "_") from the bundle name of the result resource bundle.
+     * As above, candidate bundle names where the final component is an empty string are omitted.
+     * With each of the candidate bundle names it attempts to instantiate a resource bundle, as
+     * described above.
+     * Whenever it succeeds, it calls the previously instantiated resource
+     * bundle's {@link #setParent(benchmarks.instrumented.java.util.ResourceBundle) setParent} method
+     * with the new resource bundle, unless the previously instantiated resource
+     * bundle already has a non-null parent.
+     *
+     * <p>
+     * Implementations of <code>getBundle</code> may cache instantiated resource bundles
+     * and return the same resource bundle instance multiple times. They may also
+     * vary the sequence in which resource bundles are instantiated as long as the
+     * selection of the result resource bundle and its parent chain are compatible with
+     * the description above.
+     *
+     * <p>
+     * The <code>baseName</code> argument should be a fully qualified class name. However, for
+     * compatibility with earlier versions, Sun's Java 2 runtime environments do not verify this,
+     * and so it is possible to access <code>PropertyResourceBundle</code>s by specifying a
+     * path name (using "/") instead of a fully qualified class name (using ".").
+     *
+     * <p>
+     * <strong>Example:</strong> The following class and property files are provided:
+     * MyResources.class, MyResources_fr_CH.properties, MyResources_fr_CH.class,
+     * MyResources_fr.properties, MyResources_en.properties, MyResources_es_ES.class.
+     * The contents of all files are valid (that is, public non-abstract subclasses of ResourceBundle for
+     * the ".class" files, syntactically correct ".properties" files).
+     * The default locale is <code>Locale("en", "GB")</code>.
+     * <p>
+     * Calling <code>getBundle</code> with the shown locale argument values instantiates
+     * resource bundles from the following sources:
+     * <ul>
+     * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class
+     * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent MyResources.class
+     * <li>Locale("de", "DE"): result MyResources_en.properties, parent MyResources.class
+     * <li>Locale("en", "US"): result MyResources_en.properties, parent MyResources.class
+     * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent MyResources.class
+     * </ul>
+     * The file MyResources_fr_CH.properties is never used because it is hidden by
+     * MyResources_fr_CH.class.
+     *
+     * <p>
+     *
+     * @param baseName the base name of the resource bundle, a fully qualified class name
+     * @param locale the locale for which a resource bundle is desired
+     * @param loader the class loader from which to load the resource bundle
+     * @exception java.lang.NullPointerException
+     *     if <code>baseName</code>, <code>locale</code>, or <code>loader</code> is <code>null</code>
+     * @exception MissingResourceException
+     *     if no resource bundle for the specified base name can be found
+     * @return a resource bundle for the given base name and locale
+     * @since 1.2
+     */
+    public static ResourceBundle getBundle(String baseName, Locale locale,
+                                           ClassLoader loader)
+    {
+        if (loader == null) {
+            throw new NullPointerException();
+        }
+        return getBundleImpl(baseName, locale, loader);
+    }
+
+    private static ResourceBundle getBundleImpl(String baseName, Locale locale,
+                                                ClassLoader loader)
+    {
+        if (baseName == null) {
+            throw new NullPointerException();
+        }
+
+        //fast path the case where the bundle is cached
+        String bundleName = baseName;
+        String localeSuffix = locale.toString();
+        if (localeSuffix.length() > 0) {
+            bundleName += "_" + localeSuffix;
+        } else if (locale.getVariant().length() > 0) {
+            //This corrects some strange behavior in Locale where
+            //new Locale("", "", "VARIANT").toString == ""
+            bundleName += "___" + locale.getVariant();
+        }
+
+        // The default locale may influence the lookup result, and
+        // it may change, so we get it here once.
+        Locale defaultLocale = Locale.getDefault();
+
+        Object lookup = findBundleInCache(loader, bundleName, defaultLocale);
+        if (lookup == NOT_FOUND) {
+            throwMissingResourceException(baseName, locale);
+        } else if (lookup != null) {
+            return (ResourceBundle)lookup;
+        }
+
+        //The bundle was not cached, so start doing lookup at the root
+        //Resources are loaded starting at the root and working toward
+        //the requested bundle.
+
+        //If findBundle returns null, we become responsible for defining
+        //the bundle, and must call putBundleInCache to complete this
+        //task.  This is critical because other threads may be waiting
+        //for us to finish.
+
+        Object parent = NOT_FOUND;
+        try {
+            //locate the root bundle and work toward the desired child
+            Object root = findBundle(loader, baseName, defaultLocale, baseName, null);
+            if (root == null) {
+                putBundleInCache(loader, baseName, defaultLocale, NOT_FOUND);
+                root = NOT_FOUND;
+            }
+
+            // Search the main branch of the search tree.
+            // We need to keep references to the bundles we find on the main path
+            // so they don't get garbage collected before we get to propagate().
+            final Vector names = calculateBundleNames(baseName, locale);
+            Vector bundlesFound = new Vector(MAX_BUNDLES_SEARCHED);
+        // if we found the root bundle and no other bundle names are needed
+        // we can stop here. We don't need to search or load anything further.
+            boolean foundInMainBranch = (root != NOT_FOUND && names.size() == 0);
+
+        if (!foundInMainBranch) {
+          parent = root;
+          for (int i = 0; i < names.size(); i++) {
+                bundleName = (String)names.elementAt(i);
+                lookup = findBundle(loader, bundleName, defaultLocale, baseName, parent);
+                bundlesFound.addElement(lookup);
+                if (lookup != null) {
+                    parent = lookup;
+                    foundInMainBranch = true;
+                }
+          }
+            }
+            parent = root;
+            if (!foundInMainBranch) {
+                //we didn't find anything on the main branch, so we do the fallback branch
+                final Vector fallbackNames = calculateBundleNames(baseName, defaultLocale);
+                for (int i = 0; i < fallbackNames.size(); i++) {
+                    bundleName = (String)fallbackNames.elementAt(i);
+                    if (names.contains(bundleName)) {
+                        //the fallback branch intersects the main branch so we can stop now.
+                        break;
+                    }
+                    lookup = findBundle(loader, bundleName, defaultLocale, baseName, parent);
+                    if (lookup != null) {
+                        parent = lookup;
+                    } else {
+                        //propagate the parent to the child.  We can do this
+                        //here because we are in the default path.
+                        putBundleInCache(loader, bundleName, defaultLocale, parent);
+                    }
+                }
+            }
+            //propagate the inheritance/fallback down through the main branch
+            parent = propagate(loader, names, bundlesFound, defaultLocale, parent);
+        } catch (Exception e) {
+            //We should never get here unless there has been a change
+            //to the code that doesn't catch it's own exceptions.
+            cleanUpConstructionList();
+            throwMissingResourceException(baseName, locale);
+        } catch (Error e) {
+            //The only Error that can currently hit this code is a ThreadDeathError
+            //but errors might be added in the future, so we'll play it safe and
+            //clean up.
+            cleanUpConstructionList();
+            throw e;
+        }
+        if (parent == NOT_FOUND) {
+            throwMissingResourceException(baseName, locale);
+        }
+        return (ResourceBundle)parent;
+    }
+
+    /**
+     * propagate bundles from the root down the specified branch of the search tree.
+     * @param loader the class loader for the bundles
+     * @param names the names of the bundles along search path
+     * @param bundlesFound the bundles corresponding to the names (some may be null)
+     * @param defaultLocale the default locale at the time getBundle was called
+     * @param parent the parent of the first bundle in the path (the root bundle)
+     * @return the value of the last bundle along the path
+     */
+    private static Object propagate(ClassLoader loader, Vector names,
+                                    Vector bundlesFound, Locale defaultLocale, Object parent) {
+        for (int i = 0; i < names.size(); i++) {
+            final String bundleName = (String)names.elementAt(i);
+            final Object lookup = bundlesFound.elementAt(i);
+            if (lookup == null) {
+                putBundleInCache(loader, bundleName, defaultLocale, parent);
+            } else {
+                parent = lookup;
+            }
+        }
+        return parent;
+    }
+
+    /** Throw a MissingResourceException with proper message */
+    private static void throwMissingResourceException(String baseName, Locale locale)
+            throws MissingResourceException{
+        throw new MissingResourceException("Can't find bundle for base name "
+                                           + baseName + ", locale " + locale,
+                                           baseName + "_" + locale,"");
+    }
+
+    /**
+     * Remove any entries this thread may have in the construction list.
+     * This is done as cleanup in the case where a bundle can't be
+     * constructed.
+     */
+    private static void cleanUpConstructionList() {
+        synchronized (cacheList) {
+            final Collection entries = underConstruction.values();
+            final Thread thisThread = Thread.currentThread();
+            while (entries.remove(thisThread)) {
+            }
+        }
+    }
+
+    /**
+     * Find a bundle in the cache or load it via the loader or a property file.
+     * If the bundle isn't found, an entry is put in the constructionCache
+     * and null is returned.  If null is returned, the caller must define the bundle
+     * by calling putBundleInCache.  This routine also propagates NOT_FOUND values
+     * from parent to child bundles when the parent is NOT_FOUND.
+     * @param loader the loader to use when loading a bundle
+     * @param bundleName the complete bundle name including locale extension
+     * @param defaultLocale the default locale at the time getBundle was called
+     * @param parent the parent of the resource bundle being loaded.  null if
+     * the bundle is a root bundle
+     * @return the bundle or null if the bundle could not be found in the cache
+     * or loaded.
+     */
+    private static Object findBundle(ClassLoader loader, String bundleName, Locale defaultLocale,
+                                     String baseName, Object parent) {
+        Object result;
+        synchronized (cacheList) {
+            // Before we do the real work of this method, see
+            // whether we need to do some housekeeping:
+            // If references to class loaders have been nulled out,
+            // remove all related information from the cache
+            Reference ref = referenceQueue.poll();
+            while (ref != null) {
+                cacheList.remove(((LoaderReference) ref).getCacheKey());
+                ref = referenceQueue.poll();
+            }
+
+            //check for bundle in cache
+            cacheKey.setKeyValues(loader, bundleName, defaultLocale);
+            result = cacheList.get(cacheKey);
+            if (result != null) {
+                cacheKey.clear();
+                return result;
+            }
+            // check to see if some other thread is building this bundle.
+            // Note that there is a rare chance that this thread is already
+            // working on this bundle, and in the process getBundle was called
+            // again, in which case we can't wait (4300693)
+            Thread builder = (Thread) underConstruction.get(cacheKey);
+            boolean beingBuilt = (builder != null && builder != Thread.currentThread());
+            //if some other thread is building the bundle...
+            if (beingBuilt) {
+                //while some other thread is building the bundle...
+                while (beingBuilt) {
+                    cacheKey.clear();
+                    try {
+                        //Wait until the bundle is complete
+                        cacheList.wait();
+                    } catch (InterruptedException e) {
+                    }
+                    cacheKey.setKeyValues(loader, bundleName, defaultLocale);
+                    beingBuilt = underConstruction.containsKey(cacheKey);
+                }
+                //if someone constructed the bundle for us, return it
+                result = cacheList.get(cacheKey);
+                if (result != null) {
+                    cacheKey.clear();
+                    return result;
+                }
+            }
+            //The bundle isn't in the cache, so we are now responsible for
+            //loading it and adding it to the cache.
+            final Object key = cacheKey.clone();
+            underConstruction.put(key, Thread.currentThread());
+            //the bundle is removed from the cache by putBundleInCache
+            cacheKey.clear();
+        }
+
+        //try loading the bundle via the class loader
+        result = loadBundle(loader, bundleName, defaultLocale);
+        if (result != null) {
+            // check whether we're still responsible for construction -
+            // a recursive call to getBundle might have handled it (4300693)
+            boolean constructing;
+            synchronized (cacheList) {
+                cacheKey.setKeyValues(loader, bundleName, defaultLocale);
+                constructing = underConstruction.get(cacheKey) == Thread.currentThread();
+                cacheKey.clear();
+            }
+            if (constructing) {
+                // set the bundle's parent and put it in the cache
+                final ResourceBundle bundle = (ResourceBundle)result;
+                if (parent != NOT_FOUND && bundle.parent == null) {
+                    bundle.setParent((ResourceBundle) parent);
+                }
+                bundle.setLocale(baseName, bundleName);
+                putBundleInCache(loader, bundleName, defaultLocale, result);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculate the bundles along the search path from the base bundle to the
+     * bundle specified by baseName and locale.
+     * @param baseName the base bundle name
+     * @param locale the locale
+     * the search path.
+     *
+     */
+    private static Vector calculateBundleNames(String baseName, Locale locale) {
+        final Vector result = new Vector(MAX_BUNDLES_SEARCHED);
+        final String language = locale.getLanguage();
+        final int languageLength = language.length();
+        final String country = locale.getCountry();
+        final int countryLength = country.length();
+        final String variant = locale.getVariant();
+        final int variantLength = variant.length();
+
+        if (languageLength + countryLength + variantLength == 0) {
+            //The locale is "", "", "".
+            return result;
+        }
+        final StringBuffer temp = new StringBuffer(baseName);
+        temp.append('_');
+        temp.append(language);
+        if (languageLength > 0) {
+            result.addElement(temp.toString());
+        }
+
+        if (countryLength + variantLength == 0) {
+            return result;
+        }
+        temp.append('_');
+        temp.append(country);
+        if (countryLength > 0) {
+            result.addElement(temp.toString());
+        }
+
+        if (variantLength == 0) {
+            return result;
+        }
+        temp.append('_');
+        temp.append(variant);
+        result.addElement(temp.toString());
+
+        return result;
+    }
+
+    /**
+     * Find a bundle in the cache.
+     * @param loader the class loader that is responsible for loading the bundle.
+     * @param bundleName the complete name of the bundle including locale extension.
+     *      ex. sun.text.resources.LocaleElements_fr_BE
+     * @param defaultLocale the default locale at the time getBundle was called
+     * @return the cached bundle.  null if the bundle is not in the cache.
+     */
+    private static Object findBundleInCache(ClassLoader loader, String bundleName,
+                                            Locale defaultLocale) {
+        //Synchronize access to cacheList, cacheKey, and underConstruction
+        synchronized (cacheList) {
+            cacheKey.setKeyValues(loader, bundleName, defaultLocale);
+            Object result = cacheList.get(cacheKey);
+            cacheKey.clear();
+            return result;
+        }
+    }
+
+    /**
+     * Put a new bundle in the cache and notify waiting threads that a new
+     * bundle has been put in the cache.
+     * @param defaultLocale the default locale at the time getBundle was called
+     */
+    private static void putBundleInCache(ClassLoader loader, String bundleName,
+                                         Locale defaultLocale, Object value) {
+        //we use a static shared cacheKey but we use the lock in cacheList since
+        //the key is only used to interact with cacheList.
+        synchronized (cacheList) {
+            cacheKey.setKeyValues(loader, bundleName, defaultLocale);
+            cacheList.put(cacheKey.clone(), value);
+            underConstruction.remove(cacheKey);
+            cacheKey.clear();
+            //notify waiters that we're done constructing the bundle
+            cacheList.notifyAll();
+        }
+    }
+
+    /**
+     * Load a bundle through either the specified ClassLoader or from a ".properties" file
+     * and return the loaded bundle.
+     * @param loader the ClassLoader to use to load the bundle.  If null, the system
+     *      ClassLoader is used.
+     * @param bundleName the name of the resource to load.  The name should be complete
+     *      including a qualified class name followed by the locale extension.
+     *      ex. sun.text.resources.LocaleElements_fr_BE
+     * @param defaultLocale the default locale at the time getBundle was called
+     * @return the bundle or null if none could be found.
+     */
+    private static Object loadBundle(final ClassLoader loader, String bundleName, Locale defaultLocale) {
+
+    // replace any '/'s with '.'s for the bug-for-bug compatible
+    // ClassLoader behavior. (5077272, see also 4872868)
+    String correctedBundleName = bundleName.replace('/', '.');
+
+        // Search for class file using class loader
+        try {
+            Class bundleClass;
+            if (loader != null) {
+                bundleClass = loader.loadClass(correctedBundleName);
+            } else {
+                bundleClass = Class.forName(correctedBundleName);
+            }
+            if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
+                Object myBundle = bundleClass.newInstance();
+                // Creating the instance may have triggered a recursive call to getBundle,
+                // in which case the bundle created by the recursive call would be in the
+                // cache now (4300693). For consistency, we'd then return the bundle from the cache.
+                Object otherBundle = findBundleInCache(loader, bundleName, defaultLocale);
+                if (otherBundle != null) {
+                    return otherBundle;
+                } else {
+                    return myBundle;
+                }
+            }
+        } catch (Exception e) {
+        } catch (LinkageError e) {
+        }
+
+        // Next search for a Properties file.
+        final String resName = bundleName.replace('.', '/') + ".properties";
+        InputStream stream = (InputStream)java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction() {
+                public Object run() {
+                    if (loader != null) {
+                        return loader.getResourceAsStream(resName);
+                    } else {
+                        return ClassLoader.getSystemResourceAsStream(resName);
+                    }
+                }
+            }
+        );
+
+        if (stream != null) {
+            // make sure it is buffered
+            stream = new java.io.BufferedInputStream(stream);
+            try {
+                return new PropertyResourceBundle(stream);
+            } catch (Exception e) {
+            } finally {
+                try {
+                    stream.close();
+                } catch (Exception e) {
+                    // to avoid propagating an IOException back into the caller
+                    // (I'm assuming this is never going to happen, and if it does,
+                    // I'm obeying the precedent of swallowing exceptions set by the
+                    // existing code above)
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets an object for the given key from this resource bundle.
+     * Returns null if this resource bundle does not contain an
+     * object for the given key.
+     *
+     * @param key the key for the desired object
+     * @exception NullPointerException if <code>key</code> is <code>null</code>
+     * @return the object for the given key, or null
+     */
+    protected abstract Object handleGetObject(String key);
+
+    /**
+     * Returns an enumeration of the keys.
+     *
+     */
+    public abstract Enumeration getKeys();
+}