Coverage Report - com.sun.javafx.runtime.util.backport.ResourceBundle
 
Classes in this File Line Coverage Branch Coverage Complexity
ResourceBundle
56%
149/268
38%
85/222
0
ResourceBundle$1
25%
1/4
N/A
0
ResourceBundle$BundleReference
100%
4/4
N/A
0
ResourceBundle$CacheKey
59%
39/66
37%
14/38
0
ResourceBundle$CacheKeyReference
N/A
N/A
0
ResourceBundle$Control
32%
36/113
21%
15/72
0
ResourceBundle$Control$1
0%
0/12
0%
0/6
0
ResourceBundle$LoaderReference
75%
3/4
N/A
0
ResourceBundle$NoFallbackControl
0%
0/9
0%
0/4
0
ResourceBundle$RBClassLoader
0%
0/14
0%
0/6
0
ResourceBundle$RBClassLoader$1
0%
0/2
N/A
0
ResourceBundle$SingleFormatControl
0%
0/9
0%
0/2
0
 
 1  
 /*
 2  
  * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 3  
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 4  
  *
 5  
  * This code is free software; you can redistribute it and/or modify it
 6  
  * under the terms of the GNU General Public License version 2 only, as
 7  
  * published by the Free Software Foundation.  Sun designates this
 8  
  * particular file as subject to the "Classpath" exception as provided
 9  
  * by Sun in the LICENSE file that accompanied this code.
 10  
  *
 11  
  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  
  * version 2 for more details (a copy is included in the LICENSE file that
 15  
  * accompanied this code).
 16  
  *
 17  
  * You should have received a copy of the GNU General Public License version
 18  
  * 2 along with this work; if not, write to the Free Software Foundation,
 19  
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  
  *
 21  
  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 22  
  * CA 95054 USA or visit www.sun.com if you need additional information or
 23  
  * have any questions.
 24  
  */
 25  
 
 26  
 /*
 27  
  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
 28  
  * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
 29  
  *
 30  
  * The original version of this source code and documentation
 31  
  * is copyrighted and owned by Taligent, Inc., a wholly-owned
 32  
  * subsidiary of IBM. These materials are provided under terms
 33  
  * of a License Agreement between Taligent and Sun. This technology
 34  
  * is protected by multiple US and International patents.
 35  
  *
 36  
  * This notice and attribution to Taligent may not be removed.
 37  
  * Taligent is a registered trademark of Taligent, Inc.
 38  
  *
 39  
  */
 40  
 
 41  
 /*
 42  
  * NOTE:
 43  
  * 
 44  
  * This class was backported from the JDK6 runtime library, because some of
 45  
  * the functionality in this class aren't available on JDK5, which is at
 46  
  * the moment the target JRE environment for running JavaFX applications.
 47  
  * Once JDK5 is not a supported platform anymore, this class should be
 48  
  * removed.
 49  
  */
 50  
 
 51  
 package com.sun.javafx.runtime.util.backport;
 52  
 
 53  
 import java.io.IOException;
 54  
 import java.io.InputStream;
 55  
 import java.lang.ref.ReferenceQueue;
 56  
 import java.lang.ref.SoftReference;
 57  
 import java.lang.ref.WeakReference;
 58  
 import java.net.JarURLConnection;
 59  
 import java.net.URL;
 60  
 import java.net.URLConnection;
 61  
 import java.security.AccessController;
 62  
 import java.security.PrivilegedAction;
 63  
 import java.security.PrivilegedActionException;
 64  
 import java.security.PrivilegedExceptionAction;
 65  
 import java.util.concurrent.ConcurrentHashMap;
 66  
 import java.util.concurrent.ConcurrentMap;
 67  
 import java.util.jar.JarEntry;
 68  
 import java.util.*;
 69  
 
 70  
 
 71  
 /**
 72  
  *
 73  
  * Resource bundles contain locale-specific objects.
 74  
  * When your program needs a locale-specific resource,
 75  
  * a <code>String</code> for example, your program can load it
 76  
  * from the resource bundle that is appropriate for the
 77  
  * current user's locale. In this way, you can write
 78  
  * program code that is largely independent of the user's
 79  
  * locale isolating most, if not all, of the locale-specific
 80  
  * information in resource bundles.
 81  
  *
 82  
  * <p>
 83  
  * This allows you to write programs that can:
 84  
  * <UL type=SQUARE>
 85  
  * <LI> be easily localized, or translated, into different languages
 86  
  * <LI> handle multiple locales at once
 87  
  * <LI> be easily modified later to support even more locales
 88  
  * </UL>
 89  
  *
 90  
  * <P>
 91  
  * Resource bundles belong to families whose members share a common base 
 92  
  * name, but whose names also have additional components that identify 
 93  
  * their locales. For example, the base name of a family of resource 
 94  
  * bundles might be "MyResources". The family should have a default 
 95  
  * resource bundle which simply has the same name as its family - 
 96  
  * "MyResources" - and will be used as the bundle of last resort if a
 97  
  * specific locale is not supported. The family can then provide as
 98  
  * many locale-specific members as needed, for example a German one
 99  
  * named "MyResources_de".
 100  
  *
 101  
  * <P>
 102  
  * Each resource bundle in a family contains the same items, but the items have
 103  
  * been translated for the locale represented by that resource bundle.
 104  
  * For example, both "MyResources" and "MyResources_de" may have a
 105  
  * <code>String</code> that's used on a button for canceling operations.
 106  
  * In "MyResources" the <code>String</code> may contain "Cancel" and in
 107  
  * "MyResources_de" it may contain "Abbrechen".
 108  
  *
 109  
  * <P>
 110  
  * If there are different resources for different countries, you
 111  
  * can make specializations: for example, "MyResources_de_CH" contains objects for
 112  
  * the German language (de) in Switzerland (CH). If you want to only
 113  
  * modify some of the resources
 114  
  * in the specialization, you can do so.
 115  
  *
 116  
  * <P>
 117  
  * When your program needs a locale-specific object, it loads
 118  
  * the <code>ResourceBundle</code> class using the
 119  
  * {@link #getBundle(java.lang.String, java.util.Locale) getBundle}
 120  
  * method:
 121  
  * <blockquote>
 122  
  * <pre>
 123  
  * ResourceBundle myResources =
 124  
  *      ResourceBundle.getBundle("MyResources", currentLocale);
 125  
  * </pre>
 126  
  * </blockquote>
 127  
  *
 128  
  * <P>
 129  
  * Resource bundles contain key/value pairs. The keys uniquely
 130  
  * identify a locale-specific object in the bundle. Here's an
 131  
  * example of a <code>ListResourceBundle</code> that contains
 132  
  * two key/value pairs:
 133  
  * <blockquote>
 134  
  * <pre>
 135  
  * public class MyResources extends ListResourceBundle {
 136  
  *     protected Object[][] getContents() {
 137  
  *         return new Object[][] {
 138  
  *             // LOCALIZE THE SECOND STRING OF EACH ARRAY (e.g., "OK")
 139  
  *             {"OkKey", "OK"},
 140  
  *             {"CancelKey", "Cancel"},
 141  
  *             // END OF MATERIAL TO LOCALIZE
 142  
  *        };
 143  
  *     }
 144  
  * }
 145  
  * </pre>
 146  
  * </blockquote>
 147  
  * Keys are always <code>String</code>s.
 148  
  * In this example, the keys are "OkKey" and "CancelKey".
 149  
  * In the above example, the values
 150  
  * are also <code>String</code>s--"OK" and "Cancel"--but
 151  
  * they don't have to be. The values can be any type of object.
 152  
  *
 153  
  * <P>
 154  
  * You retrieve an object from resource bundle using the appropriate
 155  
  * getter method. Because "OkKey" and "CancelKey"
 156  
  * are both strings, you would use <code>getString</code> to retrieve them:
 157  
  * <blockquote>
 158  
  * <pre>
 159  
  * button1 = new Button(myResources.getString("OkKey"));
 160  
  * button2 = new Button(myResources.getString("CancelKey"));
 161  
  * </pre>
 162  
  * </blockquote>
 163  
  * The getter methods all require the key as an argument and return
 164  
  * the object if found. If the object is not found, the getter method
 165  
  * throws a <code>MissingResourceException</code>.
 166  
  *
 167  
  * <P>
 168  
  * Besides <code>getString</code>, <code>ResourceBundle</code> also provides
 169  
  * a method for getting string arrays, <code>getStringArray</code>,
 170  
  * as well as a generic <code>getObject</code> method for any other
 171  
  * type of object. When using <code>getObject</code>, you'll
 172  
  * have to cast the result to the appropriate type. For example:
 173  
  * <blockquote>
 174  
  * <pre>
 175  
  * int[] myIntegers = (int[]) myResources.getObject("intList");
 176  
  * </pre>
 177  
  * </blockquote>
 178  
  *
 179  
  * <P>
 180  
  * The Java Platform provides two subclasses of <code>ResourceBundle</code>,
 181  
  * <code>ListResourceBundle</code> and <code>PropertyResourceBundle</code>,
 182  
  * that provide a fairly simple way to create resources.
 183  
  * As you saw briefly in a previous example, <code>ListResourceBundle</code>
 184  
  * manages its resource as a list of key/value pairs.
 185  
  * <code>PropertyResourceBundle</code> uses a properties file to manage
 186  
  * its resources.
 187  
  *
 188  
  * <p>
 189  
  * If <code>ListResourceBundle</code> or <code>PropertyResourceBundle</code>
 190  
  * do not suit your needs, you can write your own <code>ResourceBundle</code>
 191  
  * subclass.  Your subclasses must override two methods: <code>handleGetObject</code>
 192  
  * and <code>getKeys()</code>.
 193  
  *
 194  
  * <h4>ResourceBundle.Control</h4>
 195  
  *
 196  
  * The {@link ResourceBundle.Control} class provides information necessary
 197  
  * to perform the bundle loading process by the <code>getBundle</code>
 198  
  * factory methods that take a <code>ResourceBundle.Control</code>
 199  
  * instance. You can implement your own subclass in order to enable
 200  
  * non-standard resource bundle formats, change the search strategy, or
 201  
  * define caching parameters. Refer to the descriptions of the class and the
 202  
  * {@link #getBundle(String, Locale, ClassLoader, Control) getBundle}
 203  
  * factory method for details.
 204  
  *
 205  
  * <h4>Cache Management</h4>
 206  
  *
 207  
  * Resource bundle instances created by the <code>getBundle</code> factory
 208  
  * methods are cached by default, and the factory methods return the same
 209  
  * resource bundle instance multiple times if it has been
 210  
  * cached. <code>getBundle</code> clients may clear the cache, manage the
 211  
  * lifetime of cached resource bundle instances using time-to-live values,
 212  
  * or specify not to cache resource bundle instances. Refer to the
 213  
  * descriptions of the {@linkplain #getBundle(String, Locale, ClassLoader,
 214  
  * Control) <code>getBundle</code> factory method}, {@link
 215  
  * #clearCache(ClassLoader) clearCache}, {@link
 216  
  * Control#getTimeToLive(String, Locale)
 217  
  * ResourceBundle.Control.getTimeToLive}, and {@link
 218  
  * Control#needsReload(String, Locale, String, ClassLoader, ResourceBundle,
 219  
  * long) ResourceBundle.Control.needsReload} for details.
 220  
  *
 221  
  * <h4>Example</h4>
 222  
  *
 223  
  * The following is a very simple example of a <code>ResourceBundle</code>
 224  
  * subclass, <code>MyResources</code>, that manages two resources (for a larger number of
 225  
  * resources you would probably use a <code>Map</code>).
 226  
  * Notice that you don't need to supply a value if 
 227  
  * a "parent-level" <code>ResourceBundle</code> handles the same
 228  
  * key with the same value (as for the okKey below).
 229  
  * <blockquote>
 230  
  * <pre>
 231  
  * // default (English language, United States)
 232  
  * public class MyResources extends ResourceBundle {
 233  
  *     public Object handleGetObject(String key) {
 234  
  *         if (key.equals("okKey")) return "Ok";
 235  
  *         if (key.equals("cancelKey")) return "Cancel";
 236  
  *         return null;
 237  
  *     }
 238  
  * 
 239  
  *     public Enumeration&lt;String&gt; getKeys() {
 240  
  *         return Collections.enumeration(keySet());
 241  
  *     }
 242  
  * 
 243  
  *     // Overrides handleKeySet() so that the getKeys() implementation
 244  
  *     // can rely on the keySet() value.
 245  
  *     protected Set&lt;String&gt; handleKeySet() {
 246  
  *         return new HashSet&lt;String&gt;(Arrays.asList("okKey", "cancelKey"));
 247  
  *     }
 248  
  * }
 249  
  *
 250  
  * // German language
 251  
  * public class MyResources_de extends MyResources {
 252  
  *     public Object handleGetObject(String key) {
 253  
  *         // don't need okKey, since parent level handles it.
 254  
  *         if (key.equals("cancelKey")) return "Abbrechen";
 255  
  *         return null;
 256  
  *     }
 257  
  * 
 258  
  *     protected Set&lt;String&gt; handleKeySet() {
 259  
  *         return new HashSet&lt;String&gt;(Arrays.asList("cancelKey"));
 260  
  *     }
 261  
  * }
 262  
  * </pre>
 263  
  * </blockquote>
 264  
  * You do not have to restrict yourself to using a single family of
 265  
  * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
 266  
  * exception messages, <code>ExceptionResources</code>
 267  
  * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
 268  
  * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
 269  
  * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
 270  
  *
 271  
  * @see ListResourceBundle
 272  
  * @see PropertyResourceBundle
 273  
  * @see MissingResourceException
 274  
  * @since JDK1.1
 275  
  */
 276  220
 public abstract class ResourceBundle {
 277  
 
 278  8
     private static final Locale ROOTLOCALE = new Locale("");
 279  
 
 280  
     /** initial size of the bundle cache */
 281  
     private static final int INITIAL_CACHE_SIZE = 32;
 282  
 
 283  
     /** constant indicating that no resource bundle exists */
 284  8
     private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {
 285  0
             public Enumeration<String> getKeys() { return null; }
 286  0
             protected Object handleGetObject(String key) { return null; }
 287  
             @Override
 288  0
             public String toString() { return "NONEXISTENT_BUNDLE"; }
 289  
         };
 290  
 
 291  
 
 292  
     /**
 293  
      * The cache is a map from cache keys (with bundle base name, locale, and
 294  
      * class loader) to either a resource bundle or NONEXISTENT_BUNDLE wrapped by a
 295  
      * BundleReference.
 296  
      *
 297  
      * The cache is a ConcurrentMap, allowing the cache to be searched
 298  
      * concurrently by multiple threads.  This will also allow the cache keys
 299  
      * to be reclaimed along with the ClassLoaders they reference.
 300  
      *
 301  
      * This variable would be better named "cache", but we keep the old
 302  
      * name for compatibility with some workarounds for bug 4212439.
 303  
      */
 304  8
     private static final ConcurrentMap<CacheKey, BundleReference> cacheList
 305  
         = new ConcurrentHashMap<CacheKey, BundleReference>(INITIAL_CACHE_SIZE);
 306  
 
 307  
     /**
 308  
      * This ConcurrentMap is used to keep multiple threads from loading the
 309  
      * same bundle concurrently.  The table entries are <CacheKey, Thread>
 310  
      * where CacheKey is the key for the bundle that is under construction
 311  
      * and Thread is the thread that is constructing the bundle.
 312  
      * This list is manipulated in findBundleInCache and putBundleInCache.
 313  
      */
 314  8
     private static final ConcurrentMap<CacheKey, Thread> underConstruction
 315  
         = new ConcurrentHashMap<CacheKey, Thread>();
 316  
 
 317  
     /**
 318  
      * Queue for reference objects referring to class loaders or bundles.
 319  
      */
 320  8
     private static final ReferenceQueue referenceQueue = new ReferenceQueue();
 321  
 
 322  
     /**
 323  
      * The parent bundle of this bundle.
 324  
      * The parent bundle is searched by {@link #getObject getObject}
 325  
      * when this bundle does not contain a particular resource.
 326  
      */
 327  29
     protected ResourceBundle parent = null;
 328  
 
 329  
     /**
 330  
      * The locale for this bundle.
 331  
      */
 332  29
     private Locale locale = null;
 333  
 
 334  
     /**
 335  
      * The base bundle name for this bundle.
 336  
      */
 337  
     private String name;
 338  
 
 339  
     /**
 340  
      * The flag indicating this bundle has expired in the cache.
 341  
      */
 342  
     private volatile boolean expired;
 343  
 
 344  
     /**
 345  
      * The back link to the cache key. null if this bundle isn't in
 346  
      * the cache (yet) or has expired.
 347  
      */
 348  
     private volatile CacheKey cacheKey;
 349  
 
 350  
     /**
 351  
      * A Set of the keys contained only in this ResourceBundle.
 352  
      */
 353  
     private volatile Set<String> keySet;
 354  
 
 355  
     /**
 356  
      * Sole constructor.  (For invocation by subclass constructors, typically
 357  
      * implicit.)
 358  
      */
 359  29
     public ResourceBundle() {
 360  29
     }
 361  
 
 362  
     /**
 363  
      * Gets a string for the given key from this resource bundle or one of its parents.
 364  
      * Calling this method is equivalent to calling
 365  
      * <blockquote>
 366  
      * <code>(String) {@link #getObject(java.lang.String) getObject}(key)</code>.
 367  
      * </blockquote>
 368  
      *
 369  
      * @param key the key for the desired string
 370  
      * @exception NullPointerException if <code>key</code> is <code>null</code>
 371  
      * @exception MissingResourceException if no object for the given key can be found
 372  
      * @exception ClassCastException if the object found for the given key is not a string
 373  
      * @return the string for the given key
 374  
      */
 375  
     public final String getString(String key) {
 376  53
         return (String) getObject(key);
 377  
     }
 378  
 
 379  
     /**
 380  
      * Gets a string array for the given key from this resource bundle or one of its parents.
 381  
      * Calling this method is equivalent to calling
 382  
      * <blockquote>
 383  
      * <code>(String[]) {@link #getObject(java.lang.String) getObject}(key)</code>.
 384  
      * </blockquote>
 385  
      *
 386  
      * @param key the key for the desired string array
 387  
      * @exception NullPointerException if <code>key</code> is <code>null</code>
 388  
      * @exception MissingResourceException if no object for the given key can be found
 389  
      * @exception ClassCastException if the object found for the given key is not a string array
 390  
      * @return the string array for the given key
 391  
      */
 392  
     public final String[] getStringArray(String key) {
 393  0
         return (String[]) getObject(key);
 394  
     }
 395  
 
 396  
     /**
 397  
      * Gets an object for the given key from this resource bundle or one of its parents.
 398  
      * This method first tries to obtain the object from this resource bundle using
 399  
      * {@link #handleGetObject(java.lang.String) handleGetObject}.
 400  
      * If not successful, and the parent resource bundle is not null,
 401  
      * it calls the parent's <code>getObject</code> method.
 402  
      * If still not successful, it throws a MissingResourceException.
 403  
      *
 404  
      * @param key the key for the desired object
 405  
      * @exception NullPointerException if <code>key</code> is <code>null</code>
 406  
      * @exception MissingResourceException if no object for the given key can be found
 407  
      * @return the object for the given key
 408  
      */
 409  
     public final Object getObject(String key) {
 410  57
         Object obj = handleGetObject(key);
 411  57
         if (obj == null) {
 412  4
             if (parent != null) {
 413  4
                 obj = parent.getObject(key);
 414  
             }
 415  4
             if (obj == null)
 416  0
                 throw new MissingResourceException("Can't find resource for bundle "
 417  
                                                    +this.getClass().getName()
 418  
                                                    +", key "+key,
 419  
                                                    this.getClass().getName(),
 420  
                                                    key);
 421  
         }
 422  57
         return obj;
 423  
     }
 424  
 
 425  
     /**
 426  
      * Returns the locale of this resource bundle. This method can be used after a
 427  
      * call to getBundle() to determine whether the resource bundle returned really
 428  
      * corresponds to the requested locale or is a fallback.
 429  
      *
 430  
      * @return the locale of this resource bundle
 431  
      */
 432  
     public Locale getLocale() {
 433  0
         return locale;
 434  
     }
 435  
 
 436  
     /*
 437  
      * Automatic determination of the ClassLoader to be used to load
 438  
      * resources on behalf of the client.  N.B. The client is getLoader's
 439  
      * caller's caller.
 440  
      */
 441  
     private static ClassLoader getLoader() {
 442  
 //        Class[] stack = getClassContext();
 443  
         /* Magic number 2 identifies our caller's caller */
 444  
 //        Class c = stack[2];
 445  0
         Class c = com.sun.javafx.runtime.util.StringLocalization.class;
 446  0
         ClassLoader cl = (c == null) ? null : c.getClassLoader();
 447  0
         if (cl == null) {
 448  
             // When the caller's loader is the boot class loader, cl is null
 449  
             // here. In that case, ClassLoader.getSystemClassLoader() may
 450  
             // return the same class loader that the application is
 451  
             // using. We therefore use a wrapper ClassLoader to create a
 452  
             // separate scope for bundles loaded on behalf of the Java
 453  
             // runtime so that these bundles cannot be returned from the
 454  
             // cache to the application (5048280).
 455  0
             cl = RBClassLoader.INSTANCE;
 456  
         }
 457  0
         return cl;
 458  
     }
 459  
 
 460  
 //    private static native Class[] getClassContext();
 461  
 
 462  
     /**
 463  
      * A wrapper of ClassLoader.getSystemClassLoader().
 464  
      */
 465  0
     private static class RBClassLoader extends ClassLoader {
 466  0
         private static final RBClassLoader INSTANCE = AccessController.doPrivileged(
 467  0
                     new PrivilegedAction<RBClassLoader>() {
 468  
                         public RBClassLoader run() {
 469  0
                             return new RBClassLoader();
 470  
                         }
 471  
                     });
 472  0
         private static final ClassLoader loader = ClassLoader.getSystemClassLoader();
 473  
 
 474  0
         private RBClassLoader() {
 475  0
         }
 476  
         @Override
 477  
         public Class<?> loadClass(String name) throws ClassNotFoundException {
 478  0
             if (loader != null) {
 479  0
                 return loader.loadClass(name);
 480  
             }
 481  0
             return Class.forName(name);
 482  
         }
 483  
         @Override
 484  
         public URL getResource(String name) {
 485  0
             if (loader != null) {
 486  0
                 return loader.getResource(name);
 487  
             }
 488  0
             return ClassLoader.getSystemResource(name);
 489  
         }
 490  
         @Override
 491  
         public InputStream getResourceAsStream(String name) {
 492  0
             if (loader != null) {
 493  0
                 return loader.getResourceAsStream(name);
 494  
             }
 495  0
             return ClassLoader.getSystemResourceAsStream(name);
 496  
         }
 497  
     }
 498  
 
 499  
     /**
 500  
      * Sets the parent bundle of this bundle.
 501  
      * The parent bundle is searched by {@link #getObject getObject}
 502  
      * when this bundle does not contain a particular resource.
 503  
      *
 504  
      * @param parent this bundle's parent bundle.
 505  
      */
 506  
     protected void setParent(ResourceBundle parent) {
 507  26
         assert parent != NONEXISTENT_BUNDLE;
 508  26
         this.parent = parent;
 509  26
     }
 510  
 
 511  
     /**
 512  
      * Key used for cached resource bundles.  The key checks the base
 513  
      * name, the locale, and the class loader to determine if the
 514  
      * resource is a match to the requested one. The loader may be
 515  
      * null, but the base name and the locale must have a non-null
 516  
      * value.
 517  
      */
 518  232
     private static final class CacheKey implements Cloneable {
 519  
         // These three are the actual keys for lookup in Map.
 520  
         private String name;
 521  
         private Locale locale;
 522  
         private LoaderReference loaderRef;
 523  
 
 524  
         // bundle format which is necessary for calling
 525  
         // Control.needsReload().
 526  
         private String format;
 527  
 
 528  
         // These time values are in CacheKey so that NONEXISTENT_BUNDLE
 529  
         // doesn't need to be cloned for caching.
 530  
 
 531  
         // The time when the bundle has been loaded
 532  
         private volatile long loadTime;
 533  
 
 534  
         // The time when the bundle expires in the cache, or either
 535  
         // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
 536  
         private volatile long expirationTime;
 537  
 
 538  
         // Placeholder for an error report by a Throwable
 539  
         private Throwable cause;
 540  
 
 541  
         // Hash code value cache to avoid recalculating the hash code
 542  
         // of this instance.
 543  
         private int hashCodeCache;
 544  
 
 545  53
         CacheKey(String baseName, Locale locale, ClassLoader loader) {
 546  53
             this.name = baseName;
 547  53
             this.locale = locale;
 548  53
             if (loader == null) {
 549  0
                 this.loaderRef = null;
 550  
             } else {
 551  53
                 loaderRef = new LoaderReference(loader, referenceQueue, this);
 552  
             }
 553  53
             calculateHashCode();
 554  53
         }
 555  
 
 556  
         String getName() {
 557  106
             return name;
 558  
         }
 559  
 
 560  
         CacheKey setName(String baseName) {
 561  0
             if (!this.name.equals(baseName)) {
 562  0
                 this.name = baseName;
 563  0
                 calculateHashCode();
 564  
             }
 565  0
             return this;
 566  
         }
 567  
 
 568  
         Locale getLocale() {
 569  80
             return locale;
 570  
         }
 571  
 
 572  
         CacheKey setLocale(Locale locale) {
 573  150
             if (!this.locale.equals(locale)) {
 574  150
                 this.locale = locale;
 575  150
                 calculateHashCode();
 576  
             }
 577  150
             return this;
 578  
         }
 579  
 
 580  
         ClassLoader getLoader() {
 581  243
             return (loaderRef != null) ? loaderRef.get() : null;
 582  
         }
 583  
 
 584  
         @Override
 585  
         public boolean equals(Object other) {
 586  188
             if (this == other) {
 587  40
                 return true;
 588  
             }
 589  
             try {
 590  148
                 final CacheKey otherEntry = (CacheKey)other;
 591  
                 //quick check to see if they are not equal
 592  148
                 if (hashCodeCache != otherEntry.hashCodeCache) {
 593  0
                     return false;
 594  
                 }
 595  
                 //are the names the same?
 596  148
                 if (!name.equals(otherEntry.name)) {
 597  0
                     return false;
 598  
                 }
 599  
                 // are the locales the same?
 600  148
                 if (!locale.equals(otherEntry.locale)) {
 601  0
                     return false;
 602  
                 }
 603  
                 //are refs (both non-null) or (both null)?
 604  148
                 if (loaderRef == null) {
 605  0
                     return otherEntry.loaderRef == null;
 606  
                 }
 607  148
                 ClassLoader loader = loaderRef.get();
 608  148
                 return (otherEntry.loaderRef != null)
 609  
                         // with a null reference we can no longer find
 610  
                         // out which class loader was referenced; so
 611  
                         // treat it as unequal
 612  
                         && (loader != null)
 613  
                         && (loader == otherEntry.loaderRef.get());
 614  0
             } catch (NullPointerException e) {
 615  0
             } catch (ClassCastException e) {
 616  0
             }
 617  0
             return false;
 618  
         }
 619  
 
 620  
         @Override
 621  
         public int hashCode() {
 622  323
             return hashCodeCache;
 623  
         }
 624  
 
 625  
         private void calculateHashCode() {
 626  203
             hashCodeCache = name.hashCode() << 3;
 627  203
             hashCodeCache ^= locale.hashCode();
 628  203
             ClassLoader loader = getLoader();
 629  203
             if (loader != null) {
 630  203
                 hashCodeCache ^= loader.hashCode();
 631  
             }
 632  203
         }
 633  
 
 634  
         @Override
 635  
         public Object clone() {
 636  
             try {
 637  80
                 CacheKey clone = (CacheKey) super.clone();
 638  80
                 if (loaderRef != null) {
 639  80
                     clone.loaderRef = new LoaderReference(loaderRef.get(),
 640  
                                                           referenceQueue, clone);
 641  
                 }
 642  
                 // Clear the reference to a Throwable
 643  80
                 clone.cause = null;
 644  80
                 return clone;
 645  0
             } catch (CloneNotSupportedException e) {
 646  
                 //this should never happen
 647  0
                 throw new InternalError();
 648  
             }
 649  
         }
 650  
 
 651  
         String getFormat() {
 652  0
             return format;
 653  
         }
 654  
 
 655  
         void setFormat(String format) {
 656  26
             this.format = format;
 657  26
         }
 658  
 
 659  
         private void setCause(Throwable cause) {
 660  0
             if (this.cause == null) {
 661  0
                 this.cause = cause;
 662  
             } else {
 663  
                 // Override the cause if the previous one is
 664  
                 // ClassNotFoundException.
 665  0
                 if (this.cause instanceof ClassNotFoundException) {
 666  0
                     this.cause = cause;
 667  
                 }
 668  
             }
 669  0
         }
 670  
 
 671  
         private Throwable getCause() {
 672  40
             return cause;
 673  
         }
 674  
 
 675  
         @Override
 676  
         public String toString() {
 677  0
             String l = locale.toString();
 678  0
             if (l.length() == 0) {
 679  0
                 if (locale.getVariant().length() != 0) {
 680  0
                     l = "__" + locale.getVariant();
 681  
                 } else {
 682  0
                     l = "\"\"";
 683  
                 }
 684  
             }
 685  0
             return "CacheKey[" + name + ", lc=" + l + ", ldr=" + getLoader()
 686  
                 + "(format=" + format + ")]";
 687  
         }
 688  
     }
 689  
 
 690  
     /**
 691  
      * The common interface to get a CacheKey in LoaderReference and
 692  
      * BundleReference.
 693  
      */
 694  
     private static interface CacheKeyReference {
 695  
         public CacheKey getCacheKey();
 696  
     }
 697  
 
 698  
     /**
 699  
      * References to class loaders are weak references, so that they can be
 700  
      * garbage collected when nobody else is using them. The ResourceBundle
 701  
      * class has no reason to keep class loaders alive.
 702  
      */
 703  
     private static final class LoaderReference extends WeakReference<ClassLoader>
 704  
                                                implements CacheKeyReference {
 705  
         private CacheKey cacheKey;
 706  
 
 707  
         LoaderReference(ClassLoader referent, ReferenceQueue q, CacheKey key) {
 708  133
             super(referent, q);
 709  133
             cacheKey = key;
 710  133
         }
 711  
 
 712  
         public CacheKey getCacheKey() {
 713  0
             return cacheKey;
 714  
         }
 715  
     }
 716  
 
 717  
     /**
 718  
      * References to bundles are soft references so that they can be garbage
 719  
      * collected when they have no hard references.
 720  
      */
 721  
     private static final class BundleReference extends SoftReference<ResourceBundle>
 722  
                                                implements CacheKeyReference {
 723  
         private CacheKey cacheKey;
 724  
 
 725  
          BundleReference(ResourceBundle referent, ReferenceQueue q, CacheKey key) {
 726  40
             super(referent, q);
 727  40
             cacheKey = key;
 728  40
         }
 729  
 
 730  
         public CacheKey getCacheKey() {
 731  110
             return cacheKey;
 732  
         }
 733  
     }
 734  
 
 735  
     /**
 736  
      * Gets a resource bundle using the specified base name, the default locale,
 737  
      * and the caller's class loader. Calling this method is equivalent to calling
 738  
      * <blockquote>
 739  
      * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
 740  
      * </blockquote>
 741  
      * except that <code>getClassLoader()</code> is run with the security
 742  
      * privileges of <code>ResourceBundle</code>.
 743  
      * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
 744  
      * for a complete description of the search and instantiation strategy.
 745  
      *
 746  
      * @param baseName the base name of the resource bundle, a fully qualified class name
 747  
      * @exception java.lang.NullPointerException
 748  
      *     if <code>baseName</code> is <code>null</code>
 749  
      * @exception MissingResourceException
 750  
      *     if no resource bundle for the specified base name can be found
 751  
      * @return a resource bundle for the given base name and the default locale
 752  
      */
 753  
     public static final ResourceBundle getBundle(String baseName)
 754  
     {
 755  0
         return getBundleImpl(baseName, Locale.getDefault(),
 756  
                              /* must determine loader here, else we break stack invariant */
 757  
                              getLoader(),
 758  
                              Control.INSTANCE);
 759  
     }
 760  
 
 761  
     /**
 762  
      * Returns a resource bundle using the specified base name, the
 763  
      * default locale and the specified control. Calling this method
 764  
      * is equivalent to calling
 765  
      * <pre>
 766  
      * getBundle(baseName, Locale.getDefault(),
 767  
      *           this.getClass().getClassLoader(), control),
 768  
      * </pre>
 769  
      * except that <code>getClassLoader()</code> is run with the security
 770  
      * privileges of <code>ResourceBundle</code>.  See {@link
 771  
      * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
 772  
      * complete description of the resource bundle loading process with a
 773  
      * <code>ResourceBundle.Control</code>.
 774  
      *
 775  
      * @param baseName
 776  
      *        the base name of the resource bundle, a fully qualified class
 777  
      *        name
 778  
      * @param control
 779  
      *        the control which gives information for the resource bundle
 780  
      *        loading process
 781  
      * @return a resource bundle for the given base name and the default
 782  
      *        locale
 783  
      * @exception NullPointerException
 784  
      *        if <code>baseName</code> or <code>control</code> is
 785  
      *        <code>null</code>
 786  
      * @exception MissingResourceException
 787  
      *        if no resource bundle for the specified base name can be found
 788  
      * @exception IllegalArgumentException
 789  
      *        if the given <code>control</code> doesn't perform properly
 790  
      *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
 791  
      *        Note that validation of <code>control</code> is performed as
 792  
      *        needed.
 793  
      * @since 1.6
 794  
      */
 795  
     public static final ResourceBundle getBundle(String baseName,
 796  
                                                  Control control) {
 797  0
         return getBundleImpl(baseName, Locale.getDefault(),
 798  
                              /* must determine loader here, else we break stack invariant */
 799  
                              getLoader(),
 800  
                              control);
 801  
     }
 802  
 
 803  
     /**
 804  
      * Gets a resource bundle using the specified base name and locale,
 805  
      * and the caller's class loader. Calling this method is equivalent to calling
 806  
      * <blockquote>
 807  
      * <code>getBundle(baseName, locale, this.getClass().getClassLoader())</code>,
 808  
      * </blockquote>
 809  
      * except that <code>getClassLoader()</code> is run with the security
 810  
      * privileges of <code>ResourceBundle</code>.
 811  
      * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
 812  
      * for a complete description of the search and instantiation strategy.
 813  
      *
 814  
      * @param baseName
 815  
      *        the base name of the resource bundle, a fully qualified class name
 816  
      * @param locale
 817  
      *        the locale for which a resource bundle is desired
 818  
      * @exception NullPointerException
 819  
      *        if <code>baseName</code> or <code>locale</code> is <code>null</code>
 820  
      * @exception MissingResourceException
 821  
      *        if no resource bundle for the specified base name can be found
 822  
      * @return a resource bundle for the given base name and locale
 823  
      */
 824  
     public static final ResourceBundle getBundle(String baseName,
 825  
                                                  Locale locale)
 826  
     {
 827  0
         return getBundleImpl(baseName, locale,
 828  
                              /* must determine loader here, else we break stack invariant */
 829  
                              getLoader(),
 830  
                              Control.INSTANCE);
 831  
     }
 832  
 
 833  
     /**
 834  
      * Returns a resource bundle using the specified base name, target
 835  
      * locale and control, and the caller's class loader. Calling this
 836  
      * method is equivalent to calling
 837  
      * <pre>
 838  
      * getBundle(baseName, targetLocale, this.getClass().getClassLoader(),
 839  
      *           control),
 840  
      * </pre>
 841  
      * except that <code>getClassLoader()</code> is run with the security
 842  
      * privileges of <code>ResourceBundle</code>.  See {@link
 843  
      * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
 844  
      * complete description of the resource bundle loading process with a
 845  
      * <code>ResourceBundle.Control</code>.
 846  
      *
 847  
      * @param baseName
 848  
      *        the base name of the resource bundle, a fully qualified
 849  
      *        class name
 850  
      * @param targetLocale
 851  
      *        the locale for which a resource bundle is desired
 852  
      * @param control
 853  
      *        the control which gives information for the resource
 854  
      *        bundle loading process
 855  
      * @return a resource bundle for the given base name and a
 856  
      *        <code>Locale</code> in <code>locales</code>
 857  
      * @exception NullPointerException
 858  
      *        if <code>baseName</code>, <code>locales</code> or
 859  
      *        <code>control</code> is <code>null</code>
 860  
      * @exception MissingResourceException
 861  
      *        if no resource bundle for the specified base name in any
 862  
      *        of the <code>locales</code> can be found.
 863  
      * @exception IllegalArgumentException
 864  
      *        if the given <code>control</code> doesn't perform properly
 865  
      *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
 866  
      *        Note that validation of <code>control</code> is performed as
 867  
      *        needed.
 868  
      * @since 1.6
 869  
      */
 870  
     public static final ResourceBundle getBundle(String baseName, Locale targetLocale,
 871  
                                                  Control control) {
 872  0
         return getBundleImpl(baseName, targetLocale,
 873  
                              /* must determine loader here, else we break stack invariant */
 874  
                              getLoader(),
 875  
                              control);
 876  
     }
 877  
 
 878  
     /**
 879  
      * Gets a resource bundle using the specified base name, locale, and class loader.
 880  
      *
 881  
      * <p><a name="default_behavior"/>
 882  
      * Conceptually, <code>getBundle</code> uses the following strategy for locating and instantiating
 883  
      * resource bundles:
 884  
      * <p>
 885  
      * <code>getBundle</code> uses the base name, the specified locale, and the default
 886  
      * locale (obtained from {@link java.util.Locale#getDefault() Locale.getDefault})
 887  
      * to generate a sequence of <a name="candidates"><em>candidate bundle names</em></a>.
 888  
      * If the specified locale's language, country, and variant are all empty
 889  
      * strings, then the base name is the only candidate bundle name.
 890  
      * Otherwise, the following sequence is generated from the attribute
 891  
      * values of the specified locale (language1, country1, and variant1)
 892  
      * and of the default locale (language2, country2, and variant2):
 893  
      * <ul>
 894  
      * <li> baseName + "_" + language1 + "_" + country1 + "_" + variant1
 895  
      * <li> baseName + "_" + language1 + "_" + country1
 896  
      * <li> baseName + "_" + language1
 897  
      * <li> baseName + "_" + language2 + "_" + country2 + "_" + variant2
 898  
      * <li> baseName + "_" + language2 + "_" + country2
 899  
      * <li> baseName + "_" + language2
 900  
      * <li> baseName
 901  
      * </ul>
 902  
      * <p>
 903  
      * Candidate bundle names where the final component is an empty string are omitted.
 904  
      * For example, if country1 is an empty string, the second candidate bundle name is omitted.
 905  
      *
 906  
      * <p>
 907  
      * <code>getBundle</code> then iterates over the candidate bundle names to find the first
 908  
      * one for which it can <em>instantiate</em> an actual resource bundle. For each candidate
 909  
      * bundle name, it attempts to create a resource bundle:
 910  
      * <ul>
 911  
      * <li>
 912  
      * First, it attempts to load a class using the candidate bundle name.
 913  
      * If such a class can be found and loaded using the specified class loader, is assignment
 914  
      * compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated,
 915  
      * <code>getBundle</code> creates a new instance of this class and uses it as the <em>result
 916  
      * resource bundle</em>.
 917  
      * <li>
 918  
      * Otherwise, <code>getBundle</code> attempts to locate a property resource file.
 919  
      * It generates a path name from the candidate bundle name by replacing all "." characters
 920  
      * with "/" and appending the string ".properties".
 921  
      * It attempts to find a "resource" with this name using
 922  
      * {@link java.lang.ClassLoader#getResource(java.lang.String) ClassLoader.getResource}.
 923  
      * (Note that a "resource" in the sense of <code>getResource</code> has nothing to do with
 924  
      * the contents of a resource bundle, it is just a container of data, such as a file.)
 925  
      * If it finds a "resource", it attempts to create a new
 926  
      * {@link PropertyResourceBundle} instance from its contents.
 927  
      * If successful, this instance becomes the <em>result resource bundle</em>.
 928  
      * </ul>
 929  
      *
 930  
      * <p>
 931  
      * If no result resource bundle has been found, a <code>MissingResourceException</code>
 932  
      * is thrown.
 933  
      *
 934  
      * <p><a name="parent_chain"/>
 935  
      * Once a result resource bundle has been found, its <em>parent chain</em> is instantiated.
 936  
      * <code>getBundle</code> iterates over the candidate bundle names that can be
 937  
      * obtained by successively removing variant, country, and language
 938  
      * (each time with the preceding "_") from the bundle name of the result resource bundle.
 939  
      * As above, candidate bundle names where the final component is an empty string are omitted.
 940  
      * With each of the candidate bundle names it attempts to instantiate a resource bundle, as
 941  
      * described above.
 942  
      * Whenever it succeeds, it calls the previously instantiated resource
 943  
      * bundle's {@link #setParent(java.util.ResourceBundle) setParent} method
 944  
      * with the new resource bundle, unless the previously instantiated resource
 945  
      * bundle already has a non-null parent.
 946  
      *
 947  
      * <p>
 948  
      * <code>getBundle</code> caches instantiated resource bundles and
 949  
      * may return the same resource bundle instance multiple
 950  
      * times.
 951  
      *
 952  
      * <p>
 953  
      * The <code>baseName</code> argument should be a fully qualified class name. However, for
 954  
      * compatibility with earlier versions, Sun's Java SE Runtime Environments do not verify this,
 955  
      * and so it is possible to access <code>PropertyResourceBundle</code>s by specifying a
 956  
      * path name (using "/") instead of a fully qualified class name (using ".").
 957  
      *
 958  
      * <p><a name="default_behavior_example"/>
 959  
      * <strong>Example:</strong><br>The following class and property files are provided:
 960  
      * <pre>
 961  
      *     MyResources.class
 962  
      *     MyResources.properties
 963  
      *     MyResources_fr.properties
 964  
      *     MyResources_fr_CH.class
 965  
      *     MyResources_fr_CH.properties
 966  
      *     MyResources_en.properties
 967  
      *     MyResources_es_ES.class
 968  
      * </pre>
 969  
      * The contents of all files are valid (that is, public non-abstract subclasses of <code>ResourceBundle</code> for
 970  
      * the ".class" files, syntactically correct ".properties" files).
 971  
      * The default locale is <code>Locale("en", "GB")</code>.
 972  
      * <p>
 973  
      * Calling <code>getBundle</code> with the shown locale argument values instantiates
 974  
      * resource bundles from the following sources:
 975  
      * <ul>
 976  
      * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class
 977  
      * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent MyResources.class
 978  
      * <li>Locale("de", "DE"): result MyResources_en.properties, parent MyResources.class
 979  
      * <li>Locale("en", "US"): result MyResources_en.properties, parent MyResources.class
 980  
      * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent MyResources.class
 981  
      * </ul>
 982  
      * <p>The file MyResources_fr_CH.properties is never used because it is hidden by
 983  
      * MyResources_fr_CH.class. Likewise, MyResources.properties is also hidden by
 984  
      * MyResources.class.
 985  
      *
 986  
      * @param baseName the base name of the resource bundle, a fully qualified class name
 987  
      * @param locale the locale for which a resource bundle is desired
 988  
      * @param loader the class loader from which to load the resource bundle
 989  
      * @return a resource bundle for the given base name and locale
 990  
      * @exception java.lang.NullPointerException
 991  
      *        if <code>baseName</code>, <code>locale</code>, or <code>loader</code> is <code>null</code>
 992  
      * @exception MissingResourceException
 993  
      *        if no resource bundle for the specified base name can be found
 994  
      * @since 1.2
 995  
      */
 996  
     public static ResourceBundle getBundle(String baseName, Locale locale,
 997  
                                            ClassLoader loader)
 998  
     {
 999  0
         if (loader == null) {
 1000  0
             throw new NullPointerException();
 1001  
         }
 1002  0
         return getBundleImpl(baseName, locale, loader, Control.INSTANCE);
 1003  
     }
 1004  
 
 1005  
     /**
 1006  
      * Returns a resource bundle using the specified base name, target
 1007  
      * locale, class loader and control. Unlike the {@linkplain
 1008  
      * #getBundle(String, Locale, ClassLoader) <code>getBundle</code>
 1009  
      * factory methods with no <code>control</code> argument}, the given
 1010  
      * <code>control</code> specifies how to locate and instantiate resource
 1011  
      * bundles. Conceptually, the bundle loading process with the given
 1012  
      * <code>control</code> is performed in the following steps.
 1013  
      *
 1014  
      * <p>
 1015  
      * <ol>
 1016  
      * <li>This factory method looks up the resource bundle in the cache for
 1017  
      * the specified <code>baseName</code>, <code>targetLocale</code> and
 1018  
      * <code>loader</code>.  If the requested resource bundle instance is
 1019  
      * found in the cache and the time-to-live periods of the instance and
 1020  
      * all of its parent instances have not expired, the instance is returned
 1021  
      * to the caller. Otherwise, this factory method proceeds with the
 1022  
      * loading process below.</li>
 1023  
      *
 1024  
      * <li>The {@link ResourceBundle.Control#getFormats(String)
 1025  
      * control.getFormats} method is called to get resource bundle formats
 1026  
      * to produce bundle or resource names. The strings
 1027  
      * <code>"java.class"</code> and <code>"java.properties"</code>
 1028  
      * designate class-based and {@linkplain PropertyResourceBundle
 1029  
      * property}-based resource bundles, respectively. Other strings
 1030  
      * starting with <code>"java."</code> are reserved for future extensions
 1031  
      * and must not be used for application-defined formats. Other strings
 1032  
      * designate application-defined formats.</li>
 1033  
      *
 1034  
      * <li>The {@link ResourceBundle.Control#getCandidateLocales(String,
 1035  
      * Locale) control.getCandidateLocales} method is called with the target
 1036  
      * locale to get a list of <em>candidate <code>Locale</code>s</em> for
 1037  
      * which resource bundles are searched.</li>
 1038  
      *
 1039  
      * <li>The {@link ResourceBundle.Control#newBundle(String, Locale,
 1040  
      * String, ClassLoader, boolean) control.newBundle} method is called to
 1041  
      * instantiate a <code>ResourceBundle</code> for the base bundle name, a
 1042  
      * candidate locale, and a format. (Refer to the note on the cache
 1043  
      * lookup below.) This step is iterated over all combinations of the
 1044  
      * candidate locales and formats until the <code>newBundle</code> method
 1045  
      * returns a <code>ResourceBundle</code> instance or the iteration has
 1046  
      * used up all the combinations. For example, if the candidate locales
 1047  
      * are <code>Locale("de", "DE")</code>, <code>Locale("de")</code> and
 1048  
      * <code>Locale("")</code> and the formats are <code>"java.class"</code>
 1049  
      * and <code>"java.properties"</code>, then the following is the
 1050  
      * sequence of locale-format combinations to be used to call
 1051  
      * <code>control.newBundle</code>.
 1052  
      *
 1053  
      * <table style="width: 50%; text-align: left; margin-left: 40px;"
 1054  
      *  border="0" cellpadding="2" cellspacing="2">
 1055  
      * <tbody><code>
 1056  
      * <tr>
 1057  
      * <td
 1058  
      * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">Locale<br>
 1059  
      * </td>
 1060  
      * <td
 1061  
      * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">format<br>
 1062  
      * </td>
 1063  
      * </tr>
 1064  
      * <tr>
 1065  
      * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")<br>
 1066  
      * </td>
 1067  
      * <td style="vertical-align: top; width: 50%;">java.class<br>
 1068  
      * </td>
 1069  
      * </tr>
 1070  
      * <tr>
 1071  
      * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")</td>
 1072  
      * <td style="vertical-align: top; width: 50%;">java.properties<br>
 1073  
      * </td>
 1074  
      * </tr>
 1075  
      * <tr>
 1076  
      * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
 1077  
      * <td style="vertical-align: top; width: 50%;">java.class</td>
 1078  
      * </tr>
 1079  
      * <tr>
 1080  
      * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
 1081  
      * <td style="vertical-align: top; width: 50%;">java.properties</td>
 1082  
      * </tr>
 1083  
      * <tr>
 1084  
      * <td style="vertical-align: top; width: 50%;">Locale("")<br>
 1085  
      * </td>
 1086  
      * <td style="vertical-align: top; width: 50%;">java.class</td>
 1087  
      * </tr>
 1088  
      * <tr>
 1089  
      * <td style="vertical-align: top; width: 50%;">Locale("")</td>
 1090  
      * <td style="vertical-align: top; width: 50%;">java.properties</td>
 1091  
      * </tr>
 1092  
      * </code></tbody>
 1093  
      * </table>
 1094  
      * </li>
 1095  
      *
 1096  
      * <li>If the previous step has found no resource bundle, proceed to
 1097  
      * Step 6. If a bundle has been found that is a base bundle (a bundle
 1098  
      * for <code>Locale("")</code>), and the candidate locale list only contained
 1099  
      * <code>Locale("")</code>, return the bundle to the caller. If a bundle
 1100  
      * has been found that is a base bundle, but the candidate locale list
 1101  
      * contained locales other than Locale(""), put the bundle on hold and
 1102  
      * proceed to Step 6. If a bundle has been found that is not a base
 1103  
      * bundle, proceed to Step 7.</li>
 1104  
      *
 1105  
      * <li>The {@link ResourceBundle.Control#getFallbackLocale(String,
 1106  
      * Locale) control.getFallbackLocale} method is called to get a fallback
 1107  
      * locale (alternative to the current target locale) to try further
 1108  
      * finding a resource bundle. If the method returns a non-null locale,
 1109  
      * it becomes the next target locale and the loading process starts over
 1110  
      * from Step 3. Otherwise, if a base bundle was found and put on hold in
 1111  
      * a previous Step 5, it is returned to the caller now. Otherwise, a
 1112  
      * MissingResourceException is thrown.</li>
 1113  
      *
 1114  
      * <li>At this point, we have found a resource bundle that's not the
 1115  
      * base bundle. If this bundle set its parent during its instantiation,
 1116  
      * it is returned to the caller. Otherwise, its <a
 1117  
      * href="./ResourceBundle.html#parent_chain">parent chain</a> is
 1118  
      * instantiated based on the list of candidate locales from which it was
 1119  
      * found. Finally, the bundle is returned to the caller.</li>
 1120  
      *
 1121  
      *
 1122  
      * </ol>
 1123  
      *
 1124  
      * <p>During the resource bundle loading process above, this factory
 1125  
      * method looks up the cache before calling the {@link
 1126  
      * Control#newBundle(String, Locale, String, ClassLoader, boolean)
 1127  
      * control.newBundle} method.  If the time-to-live period of the
 1128  
      * resource bundle found in the cache has expired, the factory method
 1129  
      * calls the {@link ResourceBundle.Control#needsReload(String, Locale,
 1130  
      * String, ClassLoader, ResourceBundle, long) control.needsReload}
 1131  
      * method to determine whether the resource bundle needs to be reloaded.
 1132  
      * If reloading is required, the factory method calls
 1133  
      * <code>control.newBundle</code> to reload the resource bundle.  If
 1134  
      * <code>control.newBundle</code> returns <code>null</code>, the factory
 1135  
      * method puts a dummy resource bundle in the cache as a mark of
 1136  
      * nonexistent resource bundles in order to avoid lookup overhead for
 1137  
      * subsequent requests. Such dummy resource bundles are under the same
 1138  
      * expiration control as specified by <code>control</code>.
 1139  
      *
 1140  
      * <p>All resource bundles loaded are cached by default. Refer to
 1141  
      * {@link Control#getTimeToLive(String,Locale)
 1142  
      * control.getTimeToLive} for details.
 1143  
      *
 1144  
      *
 1145  
      * <p>The following is an example of the bundle loading process with the
 1146  
      * default <code>ResourceBundle.Control</code> implementation.
 1147  
      * 
 1148  
      * <p>Conditions:
 1149  
      * <ul>
 1150  
      * <li>Base bundle name: <code>foo.bar.Messages</code>
 1151  
      * <li>Requested <code>Locale</code>: {@link Locale#ITALY}</li>
 1152  
      * <li>Default <code>Locale</code>: {@link Locale#FRENCH}</li>
 1153  
      * <li>Available resource bundles:
 1154  
      * <code>foo/bar/Messages_fr.properties</code> and
 1155  
      * <code>foo/bar/Messages.properties</code></li>
 1156  
      *
 1157  
      * </ul>
 1158  
      *
 1159  
      * <p>First, <code>getBundle</code> tries loading a resource bundle in
 1160  
      * the following sequence.
 1161  
      *
 1162  
      * <ul>
 1163  
      * <li>class <code>foo.bar.Messages_it_IT</code>
 1164  
      * <li>file <code>foo/bar/Messages_it_IT.properties</code>
 1165  
      * <li>class <code>foo.bar.Messages_it</code></li>
 1166  
      * <li>file <code>foo/bar/Messages_it.properties</code></li>
 1167  
      * <li>class <code>foo.bar.Messages</code></li>
 1168  
      * <li>file <code>foo/bar/Messages.properties</code></li>
 1169  
      * </ul>
 1170  
      *
 1171  
      * <p>At this point, <code>getBundle</code> finds
 1172  
      * <code>foo/bar/Messages.properties</code>, which is put on hold
 1173  
      * because it's the base bundle.  <code>getBundle</code> calls {@link
 1174  
      * Control#getFallbackLocale(String, Locale)
 1175  
      * control.getFallbackLocale("foo.bar.Messages", Locale.ITALY)} which
 1176  
      * returns <code>Locale.FRENCH</code>. Next, <code>getBundle</code>
 1177  
      * tries loading a bundle in the following sequence.
 1178  
      *
 1179  
      * <ul>
 1180  
      * <li>class <code>foo.bar.Messages_fr</code></li>
 1181  
      * <li>file <code>foo/bar/Messages_fr.properties</code></li>
 1182  
      * <li>class <code>foo.bar.Messages</code></li>
 1183  
      * <li>file <code>foo/bar/Messages.properties</code></li>
 1184  
      * </ul>
 1185  
      *
 1186  
      * <p><code>getBundle</code> finds
 1187  
      * <code>foo/bar/Messages_fr.properties</code> and creates a
 1188  
      * <code>ResourceBundle</code> instance. Then, <code>getBundle</code>
 1189  
      * sets up its parent chain from the list of the candiate locales.  Only
 1190  
      * <code>foo/bar/Messages.properties</code> is found in the list and
 1191  
      * <code>getBundle</code> creates a <code>ResourceBundle</code> instance
 1192  
      * that becomes the parent of the instance for
 1193  
      * <code>foo/bar/Messages_fr.properties</code>.
 1194  
      *
 1195  
      * @param baseName
 1196  
      *        the base name of the resource bundle, a fully qualified
 1197  
      *        class name
 1198  
      * @param targetLocale
 1199  
      *        the locale for which a resource bundle is desired
 1200  
      * @param loader
 1201  
      *        the class loader from which to load the resource bundle
 1202  
      * @param control
 1203  
      *        the control which gives information for the resource
 1204  
      *        bundle loading process
 1205  
      * @return a resource bundle for the given base name and locale
 1206  
      * @exception NullPointerException
 1207  
      *        if <code>baseName</code>, <code>targetLocale</code>,
 1208  
      *        <code>loader</code>, or <code>control</code> is
 1209  
      *        <code>null</code>
 1210  
      * @exception MissingResourceException
 1211  
      *        if no resource bundle for the specified base name can be found
 1212  
      * @exception IllegalArgumentException
 1213  
      *        if the given <code>control</code> doesn't perform properly
 1214  
      *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
 1215  
      *        Note that validation of <code>control</code> is performed as
 1216  
      *        needed.
 1217  
      * @since 1.6
 1218  
      */
 1219  
     public static ResourceBundle getBundle(String baseName, Locale targetLocale,
 1220  
                                            ClassLoader loader, Control control) {
 1221  53
         if (loader == null || control == null) {
 1222  0
             throw new NullPointerException();
 1223  
         }
 1224  53
         return getBundleImpl(baseName, targetLocale, loader, control);
 1225  
     }
 1226  
 
 1227  
     private static ResourceBundle getBundleImpl(String baseName, Locale locale,
 1228  
                                                 ClassLoader loader, Control control) {
 1229  53
         if (locale == null || control == null) {
 1230  0
             throw new NullPointerException();
 1231  
         }
 1232  
 
 1233  
         // We create a CacheKey here for use by this call. The base
 1234  
         // name and loader will never change during the bundle loading
 1235  
         // process. We have to make sure that the locale is set before
 1236  
         // using it as a cache key.
 1237  53
         CacheKey cacheKey = new CacheKey(baseName, locale, loader);
 1238  53
         ResourceBundle bundle = null;
 1239  
 
 1240  
         // Quick lookup of the cache.
 1241  53
         BundleReference bundleRef = cacheList.get(cacheKey);
 1242  53
         if (bundleRef != null) {
 1243  38
             bundle = bundleRef.get();
 1244  38
             bundleRef = null;
 1245  
         }
 1246  
 
 1247  
         // If this bundle and all of its parents are valid (not expired),
 1248  
         // then return this bundle. If any of the bundles is expired, we
 1249  
         // don't call control.needsReload here but instead drop into the
 1250  
         // complete loading process below.
 1251  53
         if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
 1252  1
             return bundle;
 1253  
         }
 1254  
 
 1255  
         // No valid bundle was found in the cache, so we need to load the
 1256  
         // resource bundle and its parents.
 1257  
 
 1258  52
         boolean isKnownControl = (control == Control.INSTANCE) ||
 1259  
                                    (control instanceof SingleFormatControl);
 1260  52
         List<String> formats = control.getFormats(baseName);
 1261  52
         if (!isKnownControl && !checkList(formats)) {
 1262  0
             throw new IllegalArgumentException("Invalid Control: getFormats");
 1263  
         }
 1264  
 
 1265  52
         ResourceBundle baseBundle = null;
 1266  52
         for (Locale targetLocale = locale;
 1267  60
              targetLocale != null;
 1268  8
              targetLocale = control.getFallbackLocale(baseName, targetLocale)) {
 1269  52
             List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale);
 1270  52
             if (!isKnownControl && !checkList(candidateLocales)) {
 1271  0
                 throw new IllegalArgumentException("Invalid Control: getCandidateLocales");
 1272  
             }
 1273  
 
 1274  52
             bundle = findBundle(cacheKey, candidateLocales, formats, 0, control, baseBundle);
 1275  
 
 1276  
             // If the loaded bundle is the base bundle and exactly for the
 1277  
             // requested locale or the only candidate locale, then take the
 1278  
             // bundle as the resulting one. If the loaded bundle is the base
 1279  
             // bundle, it's put on hold until we finish processing all
 1280  
             // fallback locales.
 1281  52
             if (isValidBundle(bundle)) {
 1282  52
                 boolean isBaseBundle = ROOTLOCALE.equals(bundle.locale);
 1283  52
                 if (!isBaseBundle || bundle.locale.equals(locale)
 1284  
                     || (candidateLocales.size() == 1
 1285  
                         && bundle.locale.equals(candidateLocales.get(0)))) {
 1286  0
                     break;
 1287  
                 }
 1288  
 
 1289  
                 // If the base bundle has been loaded, keep the reference in
 1290  
                 // baseBundle so that we can avoid any redundant loading in case
 1291  
                 // the control specify not to cache bundles.
 1292  8
                 if (isBaseBundle && baseBundle == null) {
 1293  8
                     baseBundle = bundle;
 1294  
                 }
 1295  
             }
 1296  
         }
 1297  
 
 1298  52
         if (bundle == null) {
 1299  0
             if (baseBundle == null) {
 1300  0
                 throwMissingResourceException(baseName, locale, cacheKey.getCause());
 1301  
             }
 1302  0
             bundle = baseBundle;
 1303  
         }
 1304  
 
 1305  52
         return bundle;
 1306  
     }
 1307  
 
 1308  
     /**
 1309  
      * Checks if the given <code>List</code> is not null, not empty,
 1310  
      * not having null in its elements.
 1311  
      */
 1312  
     private static final boolean checkList(List a) {
 1313  104
         boolean valid = (a != null && a.size() != 0);
 1314  104
         if (valid) {
 1315  104
             int size = a.size();
 1316  306
             for (int i = 0; valid && i < size; i++) {
 1317  202
                 valid = (a.get(i) != null);
 1318  
             }
 1319  
         }
 1320  104
         return valid;
 1321  
     }
 1322  
 
 1323  
     private static final ResourceBundle findBundle(CacheKey cacheKey,
 1324  
                                                    List<Locale> candidateLocales,
 1325  
                                                    List<String> formats,
 1326  
                                                    int index,
 1327  
                                                    Control control,
 1328  
                                                    ResourceBundle baseBundle) {
 1329  150
         Locale targetLocale = candidateLocales.get(index);
 1330  150
         ResourceBundle parent = null;
 1331  150
         if (index != candidateLocales.size() - 1) {
 1332  98
             parent = findBundle(cacheKey, candidateLocales, formats, index + 1,
 1333  
                                 control, baseBundle);
 1334  52
         } else if (baseBundle != null && ROOTLOCALE.equals(targetLocale)) {
 1335  0
             return baseBundle;
 1336  
         }
 1337  
 
 1338  
         // Before we do the real loading work, see whether we need to
 1339  
         // do some housekeeping: If references to class loaders or
 1340  
         // resource bundles have been nulled out, remove all related
 1341  
         // information from the cache.
 1342  
         Object ref;
 1343  150
         while ((ref = referenceQueue.poll()) != null) {
 1344  0
             cacheList.remove(((CacheKeyReference)ref).getCacheKey());
 1345  
         }
 1346  
 
 1347  
         // flag indicating the resource bundle has expired in the cache
 1348  150
         boolean expiredBundle = false;
 1349  
 
 1350  
         // First, look up the cache to see if it's in the cache, without
 1351  
         // declaring beginLoading.
 1352  150
         cacheKey.setLocale(targetLocale);
 1353  150
         ResourceBundle bundle = findBundleInCache(cacheKey, control);
 1354  150
         if (isValidBundle(bundle)) {
 1355  70
             expiredBundle = bundle.expired;
 1356  70
             if (!expiredBundle) {
 1357  
                 // If its parent is the one asked for by the candidate
 1358  
                 // locales (the runtime lookup path), we can take the cached
 1359  
                 // one. (If it's not identical, then we'd have to check the
 1360  
                 // parent's parents to be consistent with what's been
 1361  
                 // requested.)
 1362  70
                 if (bundle.parent == parent) {
 1363  70
                     return bundle;
 1364  
                 }
 1365  
                 // Otherwise, remove the cached one since we can't keep
 1366  
                 // the same bundles having different parents.
 1367  0
                 BundleReference bundleRef = cacheList.get(cacheKey);
 1368  0
                 if (bundleRef != null && bundleRef.get() == bundle) {
 1369  0
                     cacheList.remove(cacheKey, bundleRef);
 1370  
                 }
 1371  
             }
 1372  
         }
 1373  
 
 1374  80
         if (bundle != NONEXISTENT_BUNDLE) {
 1375  40
             CacheKey constKey = (CacheKey) cacheKey.clone();
 1376  
 
 1377  
             try {
 1378  
                 // Try declaring loading. If beginLoading() returns true,
 1379  
                 // then we can proceed. Otherwise, we need to take a look
 1380  
                 // at the cache again to see if someone else has loaded
 1381  
                 // the bundle and put it in the cache while we've been
 1382  
                 // waiting for other loading work to complete.
 1383  40
                 while (!beginLoading(constKey)) {
 1384  0
                     bundle = findBundleInCache(cacheKey, control);
 1385  0
                     if (bundle == null) {
 1386  0
                         continue;
 1387  
                     }
 1388  0
                     if (bundle == NONEXISTENT_BUNDLE) {
 1389  
                         // If the bundle is NONEXISTENT_BUNDLE, the bundle doesn't exist.
 1390  0
                         return parent;
 1391  
                     }
 1392  0
                     expiredBundle = bundle.expired;
 1393  0
                     if (!expiredBundle) {
 1394  0
                         if (bundle.parent == parent) {
 1395  0
                             return bundle;
 1396  
                         }
 1397  0
                         BundleReference bundleRef = cacheList.get(cacheKey);
 1398  0
                         if (bundleRef != null && bundleRef.get() == bundle) {
 1399  0
                             cacheList.remove(cacheKey, bundleRef);
 1400  
                         }
 1401  0
                     }
 1402  
                 }
 1403  
 
 1404  
                 try {
 1405  40
                     bundle = loadBundle(cacheKey, formats, control, expiredBundle);
 1406  40
                     if (bundle != null) {
 1407  26
                         if (bundle.parent == null) {
 1408  26
                             bundle.setParent(parent);
 1409  
                         }
 1410  26
                         bundle.locale = targetLocale;
 1411  26
                         bundle = putBundleInCache(cacheKey, bundle, control);
 1412  26
                         return bundle;
 1413  
                     }
 1414  
 
 1415  
                     // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle
 1416  
                     // instance for the locale.
 1417  14
                     putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control);
 1418  
                 } finally {
 1419  40
                     endLoading(constKey);
 1420  14
                 }
 1421  
             } finally {
 1422  40
                 if (constKey.getCause() instanceof InterruptedException) {
 1423  0
                     Thread.currentThread().interrupt();
 1424  
                 }
 1425  
             }
 1426  
         }
 1427  54
         assert underConstruction.get(cacheKey) != Thread.currentThread();
 1428  54
         return parent;
 1429  
     }
 1430  
 
 1431  
     private static final ResourceBundle loadBundle(CacheKey cacheKey,
 1432  
                                                    List<String> formats,
 1433  
                                                    Control control,
 1434  
                                                    boolean reload) {
 1435  40
         assert underConstruction.get(cacheKey) == Thread.currentThread();
 1436  
 
 1437  
         // Here we actually load the bundle in the order of formats
 1438  
         // specified by the getFormats() value.
 1439  40
         Locale targetLocale = cacheKey.getLocale();
 1440  
 
 1441  40
         ResourceBundle bundle = null;
 1442  40
         int size = formats.size();
 1443  54
         for (int i = 0; i < size; i++) {
 1444  40
             String format = formats.get(i);
 1445  
             try {
 1446  40
                 bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
 1447  
                                            cacheKey.getLoader(), reload);
 1448  0
             } catch (LinkageError error) {
 1449  
                 // We need to handle the LinkageError case due to
 1450  
                 // inconsistent case-sensitivity in ClassLoader.
 1451  
                 // See 6572242 for details.
 1452  0
                 cacheKey.setCause(error);
 1453  0
             } catch (Exception cause) {
 1454  0
                 cacheKey.setCause(cause);
 1455  40
             }
 1456  40
             if (bundle != null) {
 1457  
                 // Set the format in the cache key so that it can be
 1458  
                 // used when calling needsReload later.
 1459  26
                 cacheKey.setFormat(format);
 1460  26
                 bundle.name = cacheKey.getName();
 1461  26
                 bundle.locale = targetLocale;
 1462  
                 // Bundle provider might reuse instances. So we should make
 1463  
                 // sure to clear the expired flag here.
 1464  26
                 bundle.expired = false;
 1465  26
                 break;
 1466  
             }
 1467  
         }
 1468  40
         assert underConstruction.get(cacheKey) == Thread.currentThread();
 1469  
 
 1470  40
         return bundle;
 1471  
     }
 1472  
 
 1473  
     private static final boolean isValidBundle(ResourceBundle bundle) {
 1474  255
         return bundle != null && bundle != NONEXISTENT_BUNDLE;
 1475  
     }
 1476  
 
 1477  
     /**
 1478  
      * Determines whether any of resource bundles in the parent chain,
 1479  
      * including the leaf, have expired.
 1480  
      */
 1481  
     private static final boolean hasValidParentChain(ResourceBundle bundle) {
 1482  1
         long now = System.currentTimeMillis();
 1483  3
         while (bundle != null) {
 1484  2
             if (bundle.expired) {
 1485  0
                 return false;
 1486  
             }
 1487  2
             CacheKey key = bundle.cacheKey;
 1488  2
             if (key != null) {
 1489  2
                 long expirationTime = key.expirationTime;
 1490  2
                 if (expirationTime >= 0 && expirationTime <= now) {
 1491  0
                     return false;
 1492  
                 }
 1493  
             }
 1494  2
             bundle = bundle.parent;
 1495  2
         }
 1496  1
         return true;
 1497  
     }
 1498  
 
 1499  
     /**
 1500  
      * Declares the beginning of actual resource bundle loading. This method
 1501  
      * returns true if the declaration is successful and the current thread has
 1502  
      * been put in underConstruction. If someone else has already begun
 1503  
      * loading, this method waits until that loading work is complete and
 1504  
      * returns false.
 1505  
      */
 1506  
     private static final boolean beginLoading(CacheKey constKey) {
 1507  40
         Thread me = Thread.currentThread();
 1508  
         Thread worker;
 1509  
         // We need to declare by putting the current Thread (me) to
 1510  
         // underConstruction that we are working on loading the specified
 1511  
         // resource bundle. If we are already working the loading, it means
 1512  
         // that the resource loading requires a recursive call. In that case,
 1513  
         // we have to proceed. (4300693)
 1514  40
         if (((worker = underConstruction.putIfAbsent(constKey, me)) == null)
 1515  
             || worker == me) {
 1516  40
             return true;
 1517  
         }
 1518  
 
 1519  
         // If someone else is working on the loading, wait until
 1520  
         // the Thread finishes the bundle loading.
 1521  0
         synchronized (worker) {
 1522  0
             while (underConstruction.get(constKey) == worker) {
 1523  
                 try {
 1524  0
                     worker.wait();
 1525  0
                 } catch (InterruptedException e) {
 1526  
                     // record the interruption
 1527  0
                     constKey.setCause(e);
 1528  0
                 }
 1529  
             }
 1530  0
         }
 1531  0
         return false;
 1532  
     }
 1533  
 
 1534  
     /**
 1535  
      * Declares the end of the bundle loading. This method calls notifyAll
 1536  
      * for those who are waiting for this completion.
 1537  
      */
 1538  
     private static final void endLoading(CacheKey constKey) {
 1539  
         // Remove this Thread from the underConstruction map and wake up
 1540  
         // those who have been waiting for me to complete this bundle
 1541  
         // loading.
 1542  40
         Thread me = Thread.currentThread();
 1543  40
         assert (underConstruction.get(constKey) == me);
 1544  40
         underConstruction.remove(constKey);
 1545  40
         synchronized (me) {
 1546  40
             me.notifyAll();
 1547  40
         }
 1548  40
     }
 1549  
 
 1550  
     /**
 1551  
      * Throw a MissingResourceException with proper message
 1552  
      */
 1553  
     private static final void throwMissingResourceException(String baseName,
 1554  
                                                             Locale locale,
 1555  
                                                             Throwable cause) {
 1556  
         // If the cause is a MissingResourceException, avoid creating
 1557  
         // a long chain. (6355009)
 1558  0
         if (cause instanceof MissingResourceException) {
 1559  0
             cause = null;
 1560  
         }
 1561  0
         throw new MissingResourceException("Can't find bundle for base name "
 1562  
                                            + baseName + ", locale " + locale,
 1563  
                                            baseName + "_" + locale, // className
 1564  
                                            "",                      // key
 1565  
                                            cause);
 1566  
     }
 1567  
 
 1568  
     /**
 1569  
      * Finds a bundle in the cache. Any expired bundles are marked as
 1570  
      * `expired' and removed from the cache upon return.
 1571  
      *
 1572  
      * @param cacheKey the key to look up the cache
 1573  
      * @param control the Control to be used for the expiration control
 1574  
      * @return the cached bundle, or null if the bundle is not found in the
 1575  
      * cache or its parent has expired. <code>bundle.expire</code> is true
 1576  
      * upon return if the bundle in the cache has expired.
 1577  
      */
 1578  
     private static final ResourceBundle findBundleInCache(CacheKey cacheKey,
 1579  
                                                           Control control) {
 1580  150
         BundleReference bundleRef = cacheList.get(cacheKey);
 1581  150
         if (bundleRef == null) {
 1582  40
             return null;
 1583  
         }
 1584  110
         ResourceBundle bundle = bundleRef.get();
 1585  110
         if (bundle == null) {
 1586  0
             return null;
 1587  
         }
 1588  110
         ResourceBundle p = bundle.parent;
 1589  110
         assert p != NONEXISTENT_BUNDLE;
 1590  
         // If the parent has expired, then this one must also expire. We
 1591  
         // check only the immediate parent because the actual loading is
 1592  
         // done from the root (base) to leaf (child) and the purpose of
 1593  
         // checking is to propagate expiration towards the leaf. For
 1594  
         // example, if the requested locale is ja_JP_JP and there are
 1595  
         // bundles for all of the candidates in the cache, we have a list,
 1596  
         //
 1597  
         // base <- ja <- ja_JP <- ja_JP_JP
 1598  
         //
 1599  
         // If ja has expired, then it will reload ja and the list becomes a
 1600  
         // tree.
 1601  
         //
 1602  
         // base <- ja (new)
 1603  
         //  "   <- ja (expired) <- ja_JP <- ja_JP_JP
 1604  
         //
 1605  
         // When looking up ja_JP in the cache, it finds ja_JP in the cache
 1606  
         // which references to the expired ja. Then, ja_JP is marked as
 1607  
         // expired and removed from the cache. This will be propagated to
 1608  
         // ja_JP_JP.
 1609  
         //
 1610  
         // Now, it's possible, for example, that while loading new ja_JP,
 1611  
         // someone else has started loading the same bundle and finds the
 1612  
         // base bundle has expired. Then, what we get from the first
 1613  
         // getBundle call includes the expired base bundle. However, if
 1614  
         // someone else didn't start its loading, we wouldn't know if the
 1615  
         // base bundle has expired at the end of the loading process. The
 1616  
         // expiration control doesn't guarantee that the returned bundle and
 1617  
         // its parents haven't expired.
 1618  
         //
 1619  
         // We could check the entire parent chain to see if there's any in
 1620  
         // the chain that has expired. But this process may never end. An
 1621  
         // extreme case would be that getTimeToLive returns 0 and
 1622  
         // needsReload always returns true.
 1623  110
         if (p != null && p.expired) {
 1624  0
             assert bundle != NONEXISTENT_BUNDLE;
 1625  0
             bundle.expired = true;
 1626  0
             bundle.cacheKey = null;
 1627  0
             cacheList.remove(cacheKey, bundleRef);
 1628  0
             bundle = null;
 1629  
         } else {
 1630  110
             CacheKey key = bundleRef.getCacheKey();
 1631  110
             long expirationTime = key.expirationTime;
 1632  110
             if (!bundle.expired && expirationTime >= 0 &&
 1633  
                 expirationTime <= System.currentTimeMillis()) {
 1634  
                 // its TTL period has expired.
 1635  0
                 if (bundle != NONEXISTENT_BUNDLE) {
 1636  
                     // Synchronize here to call needsReload to avoid
 1637  
                     // redundant concurrent calls for the same bundle.
 1638  0
                     synchronized (bundle) {
 1639  0
                         expirationTime = key.expirationTime;
 1640  0
                         if (!bundle.expired && expirationTime >= 0 &&
 1641  
                             expirationTime <= System.currentTimeMillis()) {
 1642  
                             try {
 1643  0
                                 bundle.expired = control.needsReload(key.getName(),
 1644  
                                                                      key.getLocale(),
 1645  
                                                                      key.getFormat(),
 1646  
                                                                      key.getLoader(),
 1647  
                                                                      bundle,
 1648  
                                                                      key.loadTime);
 1649  0
                             } catch (Exception e) {
 1650  0
                                 cacheKey.setCause(e);
 1651  0
                             }
 1652  0
                             if (bundle.expired) {
 1653  
                                 // If the bundle needs to be reloaded, then
 1654  
                                 // remove the bundle from the cache, but
 1655  
                                 // return the bundle with the expired flag
 1656  
                                 // on.
 1657  0
                                 bundle.cacheKey = null;
 1658  0
                                 cacheList.remove(cacheKey, bundleRef);
 1659  
                             } else {
 1660  
                                 // Update the expiration control info. and reuse
 1661  
                                 // the same bundle instance
 1662  0
                                 setExpirationTime(key, control);
 1663  
                             }
 1664  
                         }
 1665  0
                     }
 1666  
                 } else {
 1667  
                     // We just remove NONEXISTENT_BUNDLE from the cache.
 1668  0
                     cacheList.remove(cacheKey, bundleRef);
 1669  0
                     bundle = null;
 1670  
                 }
 1671  
             }
 1672  
         }
 1673  110
         return bundle;
 1674  
     }
 1675  
 
 1676  
     /**
 1677  
      * Put a new bundle in the cache.
 1678  
      *
 1679  
      * @param cacheKey the key for the resource bundle
 1680  
      * @param bundle the resource bundle to be put in the cache
 1681  
      * @return the ResourceBundle for the cacheKey; if someone has put
 1682  
      * the bundle before this call, the one found in the cache is
 1683  
      * returned.
 1684  
      */
 1685  
     private static final ResourceBundle putBundleInCache(CacheKey cacheKey,
 1686  
                                                          ResourceBundle bundle,
 1687  
                                                          Control control) {
 1688  40
         setExpirationTime(cacheKey, control);
 1689  40
         if (cacheKey.expirationTime != Control.TTL_DONT_CACHE) {
 1690  40
             CacheKey key = (CacheKey) cacheKey.clone();
 1691  40
             BundleReference bundleRef = new BundleReference(bundle, referenceQueue, key);
 1692  40
             bundle.cacheKey = key;
 1693  
 
 1694  
             // Put the bundle in the cache if it's not been in the cache.
 1695  40
             BundleReference result = cacheList.putIfAbsent(key, bundleRef);
 1696  
 
 1697  
             // If someone else has put the same bundle in the cache before
 1698  
             // us and it has not expired, we should use the one in the cache.
 1699  40
             if (result != null) {
 1700  0
                 ResourceBundle rb = result.get();
 1701  0
                 if (rb != null && !rb.expired) {
 1702  
                     // Clear the back link to the cache key
 1703  0
                     bundle.cacheKey = null;
 1704  0
                     bundle = rb;
 1705  
                     // Clear the reference in the BundleReference so that
 1706  
                     // it won't be enqueued.
 1707  0
                     bundleRef.clear();
 1708  
                 } else {
 1709  
                     // Replace the invalid (garbage collected or expired)
 1710  
                     // instance with the valid one.
 1711  0
                     cacheList.put(key, bundleRef);
 1712  
                 }
 1713  
             }
 1714  
         }
 1715  40
         return bundle;
 1716  
     }
 1717  
 
 1718  
     private static final void setExpirationTime(CacheKey cacheKey, Control control) {
 1719  40
         long ttl = control.getTimeToLive(cacheKey.getName(),
 1720  
                                          cacheKey.getLocale());
 1721  40
         if (ttl >= 0) {
 1722  
             // If any expiration time is specified, set the time to be
 1723  
             // expired in the cache.
 1724  0
             long now = System.currentTimeMillis();
 1725  0
             cacheKey.loadTime = now;
 1726  0
             cacheKey.expirationTime = now + ttl;
 1727  0
         } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
 1728  40
             cacheKey.expirationTime = ttl;
 1729  
         } else {
 1730  0
             throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
 1731  
         }
 1732  40
     }
 1733  
 
 1734  
     /**
 1735  
      * Removes all resource bundles from the cache that have been loaded
 1736  
      * using the caller's class loader.
 1737  
      *
 1738  
      * @since 1.6
 1739  
      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
 1740  
      */
 1741  
     public static final void clearCache() {
 1742  0
         clearCache(getLoader());
 1743  0
     }
 1744  
 
 1745  
     /**
 1746  
      * Removes all resource bundles from the cache that have been loaded
 1747  
      * using the given class loader.
 1748  
      *
 1749  
      * @param loader the class loader
 1750  
      * @exception NullPointerException if <code>loader</code> is null
 1751  
      * @since 1.6
 1752  
      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
 1753  
      */
 1754  
     public static final void clearCache(ClassLoader loader) {
 1755  0
         if (loader == null) {
 1756  0
             throw new NullPointerException();
 1757  
         }
 1758  0
         Set<CacheKey> set = cacheList.keySet();
 1759  0
         for (CacheKey key : set) {
 1760  0
             if (key.getLoader() == loader) {
 1761  0
                 set.remove(key);
 1762  
             }
 1763  
         }
 1764  0
     }
 1765  
 
 1766  
     /**
 1767  
      * Gets an object for the given key from this resource bundle.
 1768  
      * Returns null if this resource bundle does not contain an
 1769  
      * object for the given key.
 1770  
      *
 1771  
      * @param key the key for the desired object
 1772  
      * @exception NullPointerException if <code>key</code> is <code>null</code>
 1773  
      * @return the object for the given key, or null
 1774  
      */
 1775  
     protected abstract Object handleGetObject(String key);
 1776  
 
 1777  
     /**
 1778  
      * Returns an enumeration of the keys.
 1779  
      *
 1780  
      * @return an <code>Enumeration</code> of the keys contained in
 1781  
      *         this <code>ResourceBundle</code> and its parent bundles.
 1782  
      */
 1783  
     public abstract Enumeration<String> getKeys();
 1784  
 
 1785  
     /**
 1786  
      * Determines whether the given <code>key</code> is contained in
 1787  
      * this <code>ResourceBundle</code> or its parent bundles.
 1788  
      *
 1789  
      * @param key
 1790  
      *        the resource <code>key</code>
 1791  
      * @return <code>true</code> if the given <code>key</code> is
 1792  
      *        contained in this <code>ResourceBundle</code> or its
 1793  
      *        parent bundles; <code>false</code> otherwise.
 1794  
      * @exception NullPointerException
 1795  
      *         if <code>key</code> is <code>null</code>
 1796  
      * @since 1.6
 1797  
      */
 1798  
     public boolean containsKey(String key) {
 1799  0
         if (key == null) {
 1800  0
             throw new NullPointerException();
 1801  
         }
 1802  0
         for (ResourceBundle rb = this; rb != null; rb = rb.parent) {
 1803  0
             if (rb.handleKeySet().contains(key)) {
 1804  0
                 return true;
 1805  
             }
 1806  
         }
 1807  0
         return false;
 1808  
     }
 1809  
 
 1810  
     /**
 1811  
      * Returns a <code>Set</code> of all keys contained in this
 1812  
      * <code>ResourceBundle</code> and its parent bundles.
 1813  
      *
 1814  
      * @return a <code>Set</code> of all keys contained in this
 1815  
      *         <code>ResourceBundle</code> and its parent bundles.
 1816  
      * @since 1.6
 1817  
      */
 1818  
     public Set<String> keySet() {
 1819  8
         Set<String> keys = new HashSet<String>();
 1820  20
         for (ResourceBundle rb = this; rb != null; rb = rb.parent) {
 1821  12
             keys.addAll(rb.handleKeySet());
 1822  
         }
 1823  8
         return keys;
 1824  
     }
 1825  
 
 1826  
     /**
 1827  
      * Returns a <code>Set</code> of the keys contained <em>only</em>
 1828  
      * in this <code>ResourceBundle</code>.
 1829  
      *
 1830  
      * <p>The default implementation returns a <code>Set</code> of the
 1831  
      * keys returned by the {@link #getKeys() getKeys} method except
 1832  
      * for the ones for which the {@link #handleGetObject(String)
 1833  
      * handleGetObject} method returns <code>null</code>. Once the
 1834  
      * <code>Set</code> has been created, the value is kept in this
 1835  
      * <code>ResourceBundle</code> in order to avoid producing the
 1836  
      * same <code>Set</code> in the next calls.  Override this method
 1837  
      * in subclass implementations for faster handling.
 1838  
      *
 1839  
      * @return a <code>Set</code> of the keys contained only in this
 1840  
      *        <code>ResourceBundle</code>
 1841  
      * @since 1.6
 1842  
      */
 1843  
     protected Set<String> handleKeySet() {
 1844  0
         if (keySet == null) {
 1845  0
             synchronized (this) {
 1846  0
                 if (keySet == null) {
 1847  0
                     Set<String> keys = new HashSet<String>();
 1848  0
                     Enumeration<String> enumKeys = getKeys();
 1849  0
                     while (enumKeys.hasMoreElements()) {
 1850  0
                         String key = enumKeys.nextElement();
 1851  0
                         if (handleGetObject(key) != null) {
 1852  0
                             keys.add(key);
 1853  
                         }
 1854  0
                     }
 1855  0
                     keySet = keys;
 1856  
                 }
 1857  0
             }
 1858  
         }
 1859  0
         return keySet;
 1860  
     }
 1861  
 
 1862  
 
 1863  
 
 1864  
     /**
 1865  
      * <code>ResourceBundle.Control</code> defines a set of callback methods
 1866  
      * that are invoked by the {@link ResourceBundle#getBundle(String,
 1867  
      * Locale, ClassLoader, Control) ResourceBundle.getBundle} factory
 1868  
      * methods during the bundle loading process. In other words, a
 1869  
      * <code>ResourceBundle.Control</code> collaborates with the factory
 1870  
      * methods for loading resource bundles. The default implementation of
 1871  
      * the callback methods provides the information necessary for the
 1872  
      * factory methods to perform the <a
 1873  
      * href="./ResourceBundle.html#default_behavior">default behavior</a>.
 1874  
      *
 1875  
      * <p>In addition to the callback methods, the {@link
 1876  
      * #toBundleName(String, Locale) toBundleName} and {@link
 1877  
      * #toResourceName(String, String) toResourceName} methods are defined
 1878  
      * primarily for convenience in implementing the callback
 1879  
      * methods. However, the <code>toBundleName</code> method could be
 1880  
      * overridden to provide different conventions in the organization and
 1881  
      * packaging of localized resources.  The <code>toResourceName</code>
 1882  
      * method is <code>final</code> to avoid use of wrong resource and class
 1883  
      * name separators.
 1884  
      *
 1885  
      * <p>Two factory methods, {@link #getControl(List)} and {@link
 1886  
      * #getNoFallbackControl(List)}, provide
 1887  
      * <code>ResourceBundle.Control</code> instances that implement common
 1888  
      * variations of the default bundle loading process.
 1889  
      *
 1890  
      * <p>The formats returned by the {@link Control#getFormats(String)
 1891  
      * getFormats} method and candidate locales returned by the {@link
 1892  
      * ResourceBundle.Control#getCandidateLocales(String, Locale)
 1893  
      * getCandidateLocales} method must be consistent in all
 1894  
      * <code>ResourceBundle.getBundle</code> invocations for the same base
 1895  
      * bundle. Otherwise, the <code>ResourceBundle.getBundle</code> methods
 1896  
      * may return unintended bundles. For example, if only
 1897  
      * <code>"java.class"</code> is returned by the <code>getFormats</code>
 1898  
      * method for the first call to <code>ResourceBundle.getBundle</code>
 1899  
      * and only <code>"java.properties"</code> for the second call, then the
 1900  
      * second call will return the class-based one that has been cached
 1901  
      * during the first call.
 1902  
      *
 1903  
      * <p>A <code>ResourceBundle.Control</code> instance must be thread-safe
 1904  
      * if it's simultaneously used by multiple threads.
 1905  
      * <code>ResourceBundle.getBundle</code> does not synchronize to call
 1906  
      * the <code>ResourceBundle.Control</code> methods. The default
 1907  
      * implementations of the methods are thread-safe.
 1908  
      *
 1909  
      * <p>Applications can specify <code>ResourceBundle.Control</code>
 1910  
      * instances returned by the <code>getControl</code> factory methods or
 1911  
      * created from a subclass of <code>ResourceBundle.Control</code> to
 1912  
      * customize the bundle loading process. The following are examples of
 1913  
      * changing the default bundle loading process.
 1914  
      *
 1915  
      * <p><b>Example 1</b>
 1916  
      *
 1917  
      * <p>The following code lets <code>ResourceBundle.getBundle</code> look
 1918  
      * up only properties-based resources.
 1919  
      *
 1920  
      * <pre>
 1921  
      * import java.util.*;
 1922  
      * import static java.util.ResourceBundle.Control.*;
 1923  
      * ...
 1924  
      * ResourceBundle bundle =
 1925  
      *   ResourceBundle.getBundle("MyResources", new Locale("fr", "CH"),
 1926  
      *                            ResourceBundle.Control.getControl(FORMAT_PROPERTIES));
 1927  
      * </pre>
 1928  
      *
 1929  
      * Given the resource bundles in the <a
 1930  
      * href="./ResourceBundle.html#default_behavior_example">example</a> in
 1931  
      * the <code>ResourceBundle.getBundle</code> description, this
 1932  
      * <code>ResourceBundle.getBundle</code> call loads
 1933  
      * <code>MyResources_fr_CH.properties</code> whose parent is
 1934  
      * <code>MyResources_fr.properties</code> whose parent is
 1935  
      * <code>MyResources.properties</code>. (<code>MyResources_fr_CH.properties</code>
 1936  
      * is not hidden, but <code>MyResources_fr_CH.class</code> is.)
 1937  
      *
 1938  
      * <p><b>Example 2</b>
 1939  
      *
 1940  
      * <p>The following is an example of loading XML-based bundles
 1941  
      * using {@link Properties#loadFromXML(java.io.InputStream)
 1942  
      * Properties.loadFromXML}.
 1943  
      *
 1944  
      * <pre>
 1945  
      * ResourceBundle rb = ResourceBundle.getBundle("Messages",
 1946  
      *     new ResourceBundle.Control() {
 1947  
      *         public List&lt;String&gt; getFormats(String baseName) {
 1948  
      *             if (baseName == null)
 1949  
      *                 throw new NullPointerException();
 1950  
      *             return Arrays.asList("xml");
 1951  
      *         }
 1952  
      *         public ResourceBundle newBundle(String baseName,
 1953  
      *                                         Locale locale,
 1954  
      *                                         String format,
 1955  
      *                                         ClassLoader loader,
 1956  
      *                                         boolean reload)
 1957  
      *                          throws IllegalAccessException,
 1958  
      *                                 InstantiationException,
 1959  
      *                                 IOException {
 1960  
      *             if (baseName == null || locale == null
 1961  
      *                   || format == null || loader == null)
 1962  
      *                 throw new NullPointerException();
 1963  
      *             ResourceBundle bundle = null;
 1964  
      *             if (format.equals("xml")) {
 1965  
      *                 String bundleName = toBundleName(baseName, locale);
 1966  
      *                 String resourceName = toResourceName(bundleName, format);
 1967  
      *                 InputStream stream = null;
 1968  
      *                 if (reload) {
 1969  
      *                     URL url = loader.getResource(resourceName);
 1970  
      *                     if (url != null) {
 1971  
      *                         URLConnection connection = url.openConnection();
 1972  
      *                         if (connection != null) {
 1973  
      *                             // Disable caches to get fresh data for
 1974  
      *                             // reloading.
 1975  
      *                             connection.setUseCaches(false);
 1976  
      *                             stream = connection.getInputStream();
 1977  
      *                         }
 1978  
      *                     }
 1979  
      *                 } else {
 1980  
      *                     stream = loader.getResourceAsStream(resourceName);
 1981  
      *                 }
 1982  
      *                 if (stream != null) {
 1983  
      *                     BufferedInputStream bis = new BufferedInputStream(stream);
 1984  
      *                     bundle = new XMLResourceBundle(bis);
 1985  
      *                     bis.close();
 1986  
      *                 }
 1987  
      *             }
 1988  
      *             return bundle;
 1989  
      *         }
 1990  
      *     });
 1991  
      *
 1992  
      * ...
 1993  
      *
 1994  
      * private static class XMLResourceBundle extends ResourceBundle {
 1995  
      *     private Properties props;
 1996  
      *     XMLResourceBundle(InputStream stream) throws IOException {
 1997  
      *         props = new Properties();
 1998  
      *         props.loadFromXML(stream);
 1999  
      *     }
 2000  
      *     protected Object handleGetObject(String key) {
 2001  
      *         return props.getProperty(key);
 2002  
      *     }
 2003  
      *     public Enumeration&lt;String&gt; getKeys() {
 2004  
      *         ...
 2005  
      *     }
 2006  
      * }
 2007  
      * </pre>
 2008  
      * 
 2009  
      * @since 1.6
 2010  
      */
 2011  52
     public static class Control {
 2012  
         /**
 2013  
          * The default format <code>List</code>, which contains the strings
 2014  
          * <code>"java.class"</code> and <code>"java.properties"</code>, in
 2015  
          * this order. This <code>List</code> is {@linkplain
 2016  
          * Collections#unmodifiableList(List) unmodifiable}.
 2017  
          *
 2018  
          * @see #getFormats(String)
 2019  
          */
 2020  8
         public static final List<String> FORMAT_DEFAULT
 2021  
             = Collections.unmodifiableList(Arrays.asList("java.class",
 2022  
                                                          "java.properties"));
 2023  
 
 2024  
         /**
 2025  
          * The class-only format <code>List</code> containing
 2026  
          * <code>"java.class"</code>. This <code>List</code> is {@linkplain
 2027  
          * Collections#unmodifiableList(List) unmodifiable}.
 2028  
          *
 2029  
          * @see #getFormats(String)
 2030  
          */
 2031  8
         public static final List<String> FORMAT_CLASS
 2032  
             = Collections.unmodifiableList(Arrays.asList("java.class"));
 2033  
 
 2034  
         /**
 2035  
          * The properties-only format <code>List</code> containing
 2036  
          * <code>"java.properties"</code>. This <code>List</code> is
 2037  
          * {@linkplain Collections#unmodifiableList(List) unmodifiable}.
 2038  
          *
 2039  
          * @see #getFormats(String)
 2040  
          */
 2041  8
         public static final List<String> FORMAT_PROPERTIES
 2042  
             = Collections.unmodifiableList(Arrays.asList("java.properties"));
 2043  
 
 2044  
         /**
 2045  
          * The time-to-live constant for not caching loaded resource bundle
 2046  
          * instances.
 2047  
          *
 2048  
          * @see #getTimeToLive(String, Locale)
 2049  
          */
 2050  
         public static final long TTL_DONT_CACHE = -1;
 2051  
 
 2052  
         /**
 2053  
          * The time-to-live constant for disabling the expiration control
 2054  
          * for loaded resource bundle instances in the cache.
 2055  
          *
 2056  
          * @see #getTimeToLive(String, Locale)
 2057  
          */
 2058  
         public static final long TTL_NO_EXPIRATION_CONTROL = -2;
 2059  
 
 2060  8
         private static final Control INSTANCE = new Control();
 2061  
 
 2062  
         /**
 2063  
          * Sole constructor. (For invocation by subclass constructors,
 2064  
          * typically implicit.)
 2065  
          */
 2066  16
         protected Control() {
 2067  16
         }
 2068  
 
 2069  
         /**
 2070  
          * Returns a <code>ResourceBundle.Control</code> in which the {@link
 2071  
          * #getFormats(String) getFormats} method returns the specified
 2072  
          * <code>formats</code>. The <code>formats</code> must be equal to
 2073  
          * one of {@link Control#FORMAT_PROPERTIES}, {@link
 2074  
          * Control#FORMAT_CLASS} or {@link
 2075  
          * Control#FORMAT_DEFAULT}. <code>ResourceBundle.Control</code>
 2076  
          * instances returned by this method are singletons and thread-safe.
 2077  
          *
 2078  
          * <p>Specifying {@link Control#FORMAT_DEFAULT} is equivalent to
 2079  
          * instantiating the <code>ResourceBundle.Control</code> class,
 2080  
          * except that this method returns a singleton.
 2081  
          *
 2082  
          * @param formats
 2083  
          *        the formats to be returned by the
 2084  
          *        <code>ResourceBundle.Control.getFormats</code> method
 2085  
          * @return a <code>ResourceBundle.Control</code> supporting the
 2086  
          *        specified <code>formats</code>
 2087  
          * @exception NullPointerException
 2088  
          *        if <code>formats</code> is <code>null</code>
 2089  
          * @exception IllegalArgumentException
 2090  
          *        if <code>formats</code> is unknown
 2091  
          */
 2092  
         public static final Control getControl(List<String> formats) {
 2093  0
             if (formats.equals(Control.FORMAT_PROPERTIES)) {
 2094  0
                 return SingleFormatControl.PROPERTIES_ONLY;
 2095  
             }
 2096  0
             if (formats.equals(Control.FORMAT_CLASS)) {
 2097  0
                 return SingleFormatControl.CLASS_ONLY;
 2098  
             }
 2099  0
             if (formats.equals(Control.FORMAT_DEFAULT)) {
 2100  0
                 return Control.INSTANCE;
 2101  
             }
 2102  0
             throw new IllegalArgumentException();
 2103  
         }
 2104  
 
 2105  
         /**
 2106  
          * Returns a <code>ResourceBundle.Control</code> in which the {@link
 2107  
          * #getFormats(String) getFormats} method returns the specified
 2108  
          * <code>formats</code> and the {@link
 2109  
          * Control#getFallbackLocale(String, Locale) getFallbackLocale}
 2110  
          * method returns <code>null</code>. The <code>formats</code> must
 2111  
          * be equal to one of {@link Control#FORMAT_PROPERTIES}, {@link
 2112  
          * Control#FORMAT_CLASS} or {@link Control#FORMAT_DEFAULT}.
 2113  
          * <code>ResourceBundle.Control</code> instances returned by this
 2114  
          * method are singletons and thread-safe.
 2115  
          *
 2116  
          * @param formats
 2117  
          *        the formats to be returned by the
 2118  
          *        <code>ResourceBundle.Control.getFormats</code> method
 2119  
          * @return a <code>ResourceBundle.Control</code> supporting the
 2120  
          *        specified <code>formats</code> with no fallback
 2121  
          *        <code>Locale</code> support
 2122  
          * @exception NullPointerException
 2123  
          *        if <code>formats</code> is <code>null</code>
 2124  
          * @exception IllegalArgumentException
 2125  
          *        if <code>formats</code> is unknown
 2126  
          */
 2127  
         public static final Control getNoFallbackControl(List<String> formats) {
 2128  0
             if (formats.equals(Control.FORMAT_DEFAULT)) {
 2129  0
                 return NoFallbackControl.NO_FALLBACK;
 2130  
             }
 2131  0
             if (formats.equals(Control.FORMAT_PROPERTIES)) {
 2132  0
                 return NoFallbackControl.PROPERTIES_ONLY_NO_FALLBACK;
 2133  
             }
 2134  0
             if (formats.equals(Control.FORMAT_CLASS)) {
 2135  0
                 return NoFallbackControl.CLASS_ONLY_NO_FALLBACK;
 2136  
             }
 2137  0
             throw new IllegalArgumentException();
 2138  
         }
 2139  
 
 2140  
         /**
 2141  
          * Returns a <code>List</code> of <code>String</code>s containing
 2142  
          * formats to be used to load resource bundles for the given
 2143  
          * <code>baseName</code>. The <code>ResourceBundle.getBundle</code>
 2144  
          * factory method tries to load resource bundles with formats in the
 2145  
          * order specified by the list. The list returned by this method
 2146  
          * must have at least one <code>String</code>. The predefined
 2147  
          * formats are <code>"java.class"</code> for class-based resource
 2148  
          * bundles and <code>"java.properties"</code> for {@linkplain
 2149  
          * PropertyResourceBundle properties-based} ones. Strings starting
 2150  
          * with <code>"java."</code> are reserved for future extensions and
 2151  
          * must not be used by application-defined formats.
 2152  
          *
 2153  
          * <p>It is not a requirement to return an immutable (unmodifiable)
 2154  
          * <code>List</code>.  However, the returned <code>List</code> must
 2155  
          * not be mutated after it has been returned by
 2156  
          * <code>getFormats</code>.
 2157  
          *
 2158  
          * <p>The default implementation returns {@link #FORMAT_DEFAULT} so
 2159  
          * that the <code>ResourceBundle.getBundle</code> factory method
 2160  
          * looks up first class-based resource bundles, then
 2161  
          * properties-based ones.
 2162  
          *
 2163  
          * @param baseName
 2164  
          *        the base name of the resource bundle, a fully qualified class
 2165  
          *        name
 2166  
          * @return a <code>List</code> of <code>String</code>s containing
 2167  
          *        formats for loading resource bundles.
 2168  
          * @exception NullPointerException
 2169  
          *        if <code>baseName</code> is null
 2170  
          * @see #FORMAT_DEFAULT
 2171  
          * @see #FORMAT_CLASS
 2172  
          * @see #FORMAT_PROPERTIES
 2173  
          */
 2174  
         public List<String> getFormats(String baseName) {
 2175  0
             if (baseName == null) {
 2176  0
                 throw new NullPointerException();
 2177  
             }
 2178  0
             return FORMAT_DEFAULT;
 2179  
         }
 2180  
 
 2181  
         /**
 2182  
          * Returns a <code>List</code> of <code>Locale</code>s as candidate
 2183  
          * locales for <code>baseName</code> and <code>locale</code>. This
 2184  
          * method is called by the <code>ResourceBundle.getBundle</code>
 2185  
          * factory method each time the factory method tries finding a
 2186  
          * resource bundle for a target <code>Locale</code>.
 2187  
          *
 2188  
          * <p>The sequence of the candidate locales also corresponds to the
 2189  
          * runtime resource lookup path (also known as the <I>parent
 2190  
          * chain</I>), if the corresponding resource bundles for the
 2191  
          * candidate locales exist and their parents are not defined by
 2192  
          * loaded resource bundles themselves.  The last element of the list
 2193  
          * must be a {@linkplain Locale#ROOT root locale} if it is desired to 
 2194  
          * have the base bundle as the terminal of the parent chain.
 2195  
          *
 2196  
          * <p>If the given locale is equal to <code>Locale.ROOT</code> (the
 2197  
          * root locale), a <code>List</code> containing only the root
 2198  
          * <code>Locale</code> must be returned. In this case, the
 2199  
          * <code>ResourceBundle.getBundle</code> factory method loads only
 2200  
          * the base bundle as the resulting resource bundle.
 2201  
          *
 2202  
          * <p>It is not a requirement to return an immutable
 2203  
          * (unmodifiable) <code>List</code>. However, the returned
 2204  
          * <code>List</code> must not be mutated after it has been
 2205  
          * returned by <code>getCandidateLocales</code>.
 2206  
          *
 2207  
          * <p>The default implementation returns a <code>List</code> containing
 2208  
          * <code>Locale</code>s in the following sequence:
 2209  
          * <pre>
 2210  
          *     Locale(language, country, variant)
 2211  
          *     Locale(language, country)
 2212  
          *     Locale(language)
 2213  
          *     Locale.ROOT
 2214  
          * </pre>
 2215  
          * where <code>language</code>, <code>country</code> and
 2216  
          * <code>variant</code> are the language, country and variant values
 2217  
          * of the given <code>locale</code>, respectively. Locales where the
 2218  
          * final component values are empty strings are omitted.
 2219  
          *
 2220  
          * <p>The default implementation uses an {@link ArrayList} that
 2221  
          * overriding implementations may modify before returning it to the
 2222  
          * caller. However, a subclass must not modify it after it has
 2223  
          * been returned by <code>getCandidateLocales</code>.
 2224  
          *
 2225  
          * <p>For example, if the given <code>baseName</code> is "Messages"
 2226  
          * and the given <code>locale</code> is
 2227  
          * <code>Locale("ja",&nbsp;"",&nbsp;"XX")</code>, then a
 2228  
          * <code>List</code> of <code>Locale</code>s:
 2229  
          * <pre>
 2230  
          *     Locale("ja", "", "XX")
 2231  
          *     Locale("ja")
 2232  
          *     Locale.ROOT
 2233  
          * </pre>
 2234  
          * is returned. And if the resource bundles for the "ja" and
 2235  
          * "" <code>Locale</code>s are found, then the runtime resource
 2236  
          * lookup path (parent chain) is:
 2237  
          * <pre>
 2238  
          *     Messages_ja -> Messages
 2239  
          * </pre>
 2240  
          *
 2241  
          * @param baseName
 2242  
          *        the base name of the resource bundle, a fully
 2243  
          *        qualified class name
 2244  
          * @param locale
 2245  
          *        the locale for which a resource bundle is desired
 2246  
          * @return a <code>List</code> of candidate
 2247  
          *        <code>Locale</code>s for the given <code>locale</code>
 2248  
          * @exception NullPointerException
 2249  
          *        if <code>baseName</code> or <code>locale</code> is
 2250  
          *        <code>null</code>
 2251  
          */
 2252  
         public List<Locale> getCandidateLocales(String baseName, Locale locale) {
 2253  52
             if (baseName == null) {
 2254  0
                 throw new NullPointerException();
 2255  
             }
 2256  52
             String language = locale.getLanguage();
 2257  52
             String country = locale.getCountry();
 2258  52
             String variant = locale.getVariant();
 2259  
 
 2260  52
             List<Locale> locales = new ArrayList<Locale>(4);
 2261  52
             if (variant.length() > 0) {
 2262  0
                 locales.add(locale);
 2263  
             }
 2264  52
             if (country.length() > 0) {
 2265  46
                 locales.add((locales.size() == 0) ?
 2266  
                             locale : new Locale(language, country, ""));
 2267  
             }
 2268  52
             if (language.length() > 0) {
 2269  52
                 locales.add((locales.size() == 0) ?
 2270  
                             locale : new Locale(language, "", ""));
 2271  
             }
 2272  52
             locales.add(ROOTLOCALE);
 2273  52
             return locales;
 2274  
         }
 2275  
 
 2276  
         /**
 2277  
          * Returns a <code>Locale</code> to be used as a fallback locale for
 2278  
          * further resource bundle searches by the
 2279  
          * <code>ResourceBundle.getBundle</code> factory method. This method
 2280  
          * is called from the factory method every time when no resulting
 2281  
          * resource bundle has been found for <code>baseName</code> and
 2282  
          * <code>locale</code>, where locale is either the parameter for
 2283  
          * <code>ResourceBundle.getBundle</code> or the previous fallback
 2284  
          * locale returned by this method.
 2285  
          *
 2286  
          * <p>The method returns <code>null</code> if no further fallback
 2287  
          * search is desired.
 2288  
          *
 2289  
          * <p>The default implementation returns the {@linkplain
 2290  
          * Locale#getDefault() default <code>Locale</code>} if the given
 2291  
          * <code>locale</code> isn't the default one.  Otherwise,
 2292  
          * <code>null</code> is returned.
 2293  
          *
 2294  
          * @param baseName
 2295  
          *        the base name of the resource bundle, a fully
 2296  
          *        qualified class name for which
 2297  
          *        <code>ResourceBundle.getBundle</code> has been
 2298  
          *        unable to find any resource bundles (except for the
 2299  
          *        base bundle)
 2300  
          * @param locale
 2301  
          *        the <code>Locale</code> for which
 2302  
          *        <code>ResourceBundle.getBundle</code> has been
 2303  
          *        unable to find any resource bundles (except for the
 2304  
          *        base bundle)
 2305  
          * @return a <code>Locale</code> for the fallback search,
 2306  
          *        or <code>null</code> if no further fallback search
 2307  
          *        is desired.
 2308  
          * @exception NullPointerException
 2309  
          *        if <code>baseName</code> or <code>locale</code>
 2310  
          *        is <code>null</code>
 2311  
          */
 2312  
         public Locale getFallbackLocale(String baseName, Locale locale) {
 2313  0
             if (baseName == null) {
 2314  0
                 throw new NullPointerException();
 2315  
             }
 2316  0
             Locale defaultLocale = Locale.getDefault();
 2317  0
             return locale.equals(defaultLocale) ? null : defaultLocale;
 2318  
         }
 2319  
 
 2320  
         /**
 2321  
          * Instantiates a resource bundle for the given bundle name of the
 2322  
          * given format and locale, using the given class loader if
 2323  
          * necessary. This method returns <code>null</code> if there is no
 2324  
          * resource bundle available for the given parameters. If a resource
 2325  
          * bundle can't be instantiated due to an unexpected error, the
 2326  
          * error must be reported by throwing an <code>Error</code> or
 2327  
          * <code>Exception</code> rather than simply returning
 2328  
          * <code>null</code>.
 2329  
          *
 2330  
          * <p>If the <code>reload</code> flag is <code>true</code>, it
 2331  
          * indicates that this method is being called because the previously
 2332  
          * loaded resource bundle has expired.
 2333  
          *
 2334  
          * <p>The default implementation instantiates a
 2335  
          * <code>ResourceBundle</code> as follows.
 2336  
          *
 2337  
          * <ul>
 2338  
          *
 2339  
          * <li>The bundle name is obtained by calling {@link
 2340  
          * #toBundleName(String, Locale) toBundleName(baseName,
 2341  
          * locale)}.</li>
 2342  
          *
 2343  
          * <li>If <code>format</code> is <code>"java.class"</code>, the
 2344  
          * {@link Class} specified by the bundle name is loaded by calling
 2345  
          * {@link ClassLoader#loadClass(String)}. Then, a
 2346  
          * <code>ResourceBundle</code> is instantiated by calling {@link
 2347  
          * Class#newInstance()}.  Note that the <code>reload</code> flag is
 2348  
          * ignored for loading class-based resource bundles in this default
 2349  
          * implementation.</li>
 2350  
          *
 2351  
          * <li>If <code>format</code> is <code>"java.properties"</code>,
 2352  
          * {@link #toResourceName(String, String) toResourceName(bundlename,
 2353  
          * "properties")} is called to get the resource name.
 2354  
          * If <code>reload</code> is <code>true</code>, {@link
 2355  
          * ClassLoader#getResource(String) load.getResource} is called
 2356  
          * to get a {@link URL} for creating a {@link
 2357  
          * URLConnection}. This <code>URLConnection</code> is used to
 2358  
          * {@linkplain URLConnection#setUseCaches(boolean) disable the
 2359  
          * caches} of the underlying resource loading layers,
 2360  
          * and to {@linkplain URLConnection#getInputStream() get an
 2361  
          * <code>InputStream</code>}.
 2362  
          * Otherwise, {@link ClassLoader#getResourceAsStream(String)
 2363  
          * loader.getResourceAsStream} is called to get an {@link
 2364  
          * InputStream}. Then, a {@link
 2365  
          * PropertyResourceBundle} is constructed with the
 2366  
          * <code>InputStream</code>.</li>
 2367  
          *
 2368  
          * <li>If <code>format</code> is neither <code>"java.class"</code>
 2369  
          * nor <code>"java.properties"</code>, an
 2370  
          * <code>IllegalArgumentException</code> is thrown.</li>
 2371  
          *
 2372  
          * </ul>
 2373  
          *
 2374  
          * @param baseName
 2375  
          *        the base bundle name of the resource bundle, a fully
 2376  
          *        qualified class name
 2377  
          * @param locale
 2378  
          *        the locale for which the resource bundle should be
 2379  
          *        instantiated
 2380  
          * @param format
 2381  
          *        the resource bundle format to be loaded
 2382  
          * @param loader
 2383  
          *        the <code>ClassLoader</code> to use to load the bundle
 2384  
          * @param reload
 2385  
          *        the flag to indicate bundle reloading; <code>true</code>
 2386  
          *        if reloading an expired resource bundle,
 2387  
          *        <code>false</code> otherwise
 2388  
          * @return the resource bundle instance,
 2389  
          *        or <code>null</code> if none could be found.
 2390  
          * @exception NullPointerException
 2391  
          *        if <code>bundleName</code>, <code>locale</code>,
 2392  
          *        <code>format</code>, or <code>loader</code> is
 2393  
          *        <code>null</code>, or if <code>null</code> is returned by
 2394  
          *        {@link #toBundleName(String, Locale) toBundleName}
 2395  
          * @exception IllegalArgumentException
 2396  
          *        if <code>format</code> is unknown, or if the resource
 2397  
          *        found for the given parameters contains malformed data.
 2398  
          * @exception ClassCastException
 2399  
          *        if the loaded class cannot be cast to <code>ResourceBundle</code>
 2400  
          * @exception IllegalAccessException
 2401  
          *        if the class or its nullary constructor is not
 2402  
          *        accessible.
 2403  
          * @exception InstantiationException
 2404  
          *        if the instantiation of a class fails for some other
 2405  
          *        reason.
 2406  
          * @exception ExceptionInInitializerError
 2407  
          *        if the initialization provoked by this method fails.
 2408  
          * @exception SecurityException
 2409  
          *        If a security manager is present and creation of new
 2410  
          *        instances is denied. See {@link Class#newInstance()}
 2411  
          *        for details.
 2412  
          * @exception IOException
 2413  
          *        if an error occurred when reading resources using
 2414  
          *        any I/O operations
 2415  
          */
 2416  
         public ResourceBundle newBundle(String baseName, Locale locale, String format,
 2417  
                                         ClassLoader loader, boolean reload)
 2418  
                     throws IllegalAccessException, InstantiationException, IOException {
 2419  0
             String bundleName = toBundleName(baseName, locale);
 2420  0
             ResourceBundle bundle = null;
 2421  0
             if (format.equals("java.class")) {
 2422  
                 try {
 2423  0
                     Class<? extends ResourceBundle> bundleClass
 2424  
                         = (Class<? extends ResourceBundle>)loader.loadClass(bundleName);
 2425  
 
 2426  
                     // If the class isn't a ResourceBundle subclass, throw a
 2427  
                     // ClassCastException.
 2428  0
                     if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
 2429  0
                         bundle = bundleClass.newInstance();
 2430  
                     } else {
 2431  0
                         throw new ClassCastException(bundleClass.getName()
 2432  
                                      + " cannot be cast to ResourceBundle");
 2433  
                     }
 2434  0
                 } catch (ClassNotFoundException e) {
 2435  0
                 }
 2436  0
             } else if (format.equals("java.properties")) {
 2437  0
                 final String resourceName = toResourceName(bundleName, "properties");
 2438  0
                 final ClassLoader classLoader = loader;
 2439  0
                 final boolean reloadFlag = reload;
 2440  0
                 InputStream stream = null;
 2441  
                 try {
 2442  0
                     stream = AccessController.doPrivileged(
 2443  0
                         new PrivilegedExceptionAction<InputStream>() {
 2444  
                             public InputStream run() throws IOException {
 2445  0
                                 InputStream is = null;
 2446  0
                                 if (reloadFlag) {
 2447  0
                                     URL url = classLoader.getResource(resourceName);
 2448  0
                                     if (url != null) {
 2449  0
                                         URLConnection connection = url.openConnection();
 2450  0
                                         if (connection != null) {
 2451  
                                             // Disable caches to get fresh data for
 2452  
                                             // reloading.
 2453  0
                                             connection.setUseCaches(false);
 2454  0
                                             is = connection.getInputStream();
 2455  
                                         }
 2456  
                                     }
 2457  0
                                 } else {
 2458  0
                                     is = classLoader.getResourceAsStream(resourceName);
 2459  
                                 }
 2460  0
                                 return is;
 2461  
                             }
 2462  
                         });
 2463  0
                 } catch (PrivilegedActionException e) {
 2464  0
                     throw (IOException) e.getException();
 2465  0
                 }
 2466  0
                 if (stream != null) {
 2467  
                     try {
 2468  
 //                        bundle = new PropertyResourceBundle(stream);
 2469  
                     } finally {
 2470  0
                         stream.close();
 2471  0
                     }
 2472  
                 }
 2473  0
             } else {
 2474  0
                 throw new IllegalArgumentException("unknown format: " + format);
 2475  
             }
 2476  0
             return bundle;
 2477  
         }
 2478  
 
 2479  
         /**
 2480  
          * Returns the time-to-live (TTL) value for resource bundles that
 2481  
          * are loaded under this
 2482  
          * <code>ResourceBundle.Control</code>. Positive time-to-live values
 2483  
          * specify the number of milliseconds a bundle can remain in the
 2484  
          * cache without being validated against the source data from which
 2485  
          * it was constructed. The value 0 indicates that a bundle must be
 2486  
          * validated each time it is retrieved from the cache. {@link
 2487  
          * #TTL_DONT_CACHE} specifies that loaded resource bundles are not
 2488  
          * put in the cache. {@link #TTL_NO_EXPIRATION_CONTROL} specifies
 2489  
          * that loaded resource bundles are put in the cache with no
 2490  
          * expiration control.
 2491  
          *
 2492  
          * <p>The expiration affects only the bundle loading process by the
 2493  
          * <code>ResourceBundle.getBundle</code> factory method.  That is,
 2494  
          * if the factory method finds a resource bundle in the cache that
 2495  
          * has expired, the factory method calls the {@link
 2496  
          * #needsReload(String, Locale, String, ClassLoader, ResourceBundle,
 2497  
          * long) needsReload} method to determine whether the resource
 2498  
          * bundle needs to be reloaded. If <code>needsReload</code> returns
 2499  
          * <code>true</code>, the cached resource bundle instance is removed
 2500  
          * from the cache. Otherwise, the instance stays in the cache,
 2501  
          * updated with the new TTL value returned by this method.
 2502  
          *
 2503  
          * <p>All cached resource bundles are subject to removal from the
 2504  
          * cache due to memory constraints of the runtime environment.
 2505  
          * Returning a large positive value doesn't mean to lock loaded
 2506  
          * resource bundles in the cache.
 2507  
          *
 2508  
          * <p>The default implementation returns {@link #TTL_NO_EXPIRATION_CONTROL}.
 2509  
          *
 2510  
          * @param baseName
 2511  
          *          the base name of the resource bundle for which the
 2512  
          *          expiration value is specified.
 2513  
          * @param locale
 2514  
          *        the locale of the resource bundle for which the
 2515  
          *        expiration value is specified.
 2516  
          * @return the time (0 or a positive millisecond offset from the
 2517  
          *        cached time) to get loaded bundles expired in the cache,
 2518  
          *        {@link #TTL_NO_EXPIRATION_CONTROL} to disable the
 2519  
          *        expiration control, or {@link #TTL_DONT_CACHE} to disable
 2520  
          *        caching.
 2521  
          * @exception NullPointerException
 2522  
          *          if <code>baseName</code> or <code>locale</code> is
 2523  
          *          <code>null</code>
 2524  
          */
 2525  
         public long getTimeToLive(String baseName, Locale locale) {
 2526  40
             if (baseName == null || locale == null) {
 2527  0
                 throw new NullPointerException();
 2528  
             }
 2529  40
             return TTL_NO_EXPIRATION_CONTROL;
 2530  
         }
 2531  
 
 2532  
         /**
 2533  
          * Determines if the expired <code>bundle</code> in the cache needs
 2534  
          * to be reloaded based on the loading time given by
 2535  
          * <code>loadTime</code> or some other criteria. The method returns
 2536  
          * <code>true</code> if reloading is required; <code>false</code>
 2537  
          * otherwise. <code>loadTime</code> is a millisecond offset since
 2538  
          * the <a href="Calendar.html#Epoch"> <code>Calendar</code>
 2539  
          * Epoch</a>.
 2540  
          *
 2541  
          * The calling <code>ResourceBundle.getBundle</code> factory method
 2542  
          * calls this method on the <code>ResourceBundle.Control</code>
 2543  
          * instance used for its current invocation, not on the instance
 2544  
          * used in the invocation that originally loaded the resource
 2545  
          * bundle.
 2546  
          *
 2547  
          * <p>The default implementation compares <code>loadTime</code> and
 2548  
          * the last modified time of the source data of the resource
 2549  
          * bundle. If it's determined that the source data has been modified
 2550  
          * since <code>loadTime</code>, <code>true</code> is
 2551  
          * returned. Otherwise, <code>false</code> is returned. This
 2552  
          * implementation assumes that the given <code>format</code> is the
 2553  
          * same string as its file suffix if it's not one of the default
 2554  
          * formats, <code>"java.class"</code> or
 2555  
          * <code>"java.properties"</code>.
 2556  
          *
 2557  
          * @param baseName
 2558  
          *        the base bundle name of the resource bundle, a
 2559  
          *        fully qualified class name
 2560  
          * @param locale
 2561  
          *        the locale for which the resource bundle
 2562  
          *        should be instantiated
 2563  
          * @param format
 2564  
          *        the resource bundle format to be loaded
 2565  
          * @param loader
 2566  
          *        the <code>ClassLoader</code> to use to load the bundle
 2567  
          * @param bundle
 2568  
          *        the resource bundle instance that has been expired
 2569  
          *        in the cache
 2570  
          * @param loadTime
 2571  
          *        the time when <code>bundle</code> was loaded and put
 2572  
          *        in the cache
 2573  
          * @return <code>true</code> if the expired bundle needs to be
 2574  
          *        reloaded; <code>false</code> otherwise.
 2575  
          * @exception NullPointerException
 2576  
          *        if <code>baseName</code>, <code>locale</code>,
 2577  
          *        <code>format</code>, <code>loader</code>, or
 2578  
          *        <code>bundle</code> is <code>null</code>
 2579  
          */
 2580  
         public boolean needsReload(String baseName, Locale locale,
 2581  
                                    String format, ClassLoader loader,
 2582  
                                    ResourceBundle bundle, long loadTime) {
 2583  0
             if (bundle == null) {
 2584  0
                 throw new NullPointerException();
 2585  
             }
 2586  0
             if (format.equals("java.class") || format.equals("java.properties")) {
 2587  0
                 format = format.substring(5);
 2588  
             }
 2589  0
             boolean result = false;
 2590  
             try {
 2591  0
                 String resourceName = toResourceName(toBundleName(baseName, locale), format);
 2592  0
                 URL url = loader.getResource(resourceName);
 2593  0
                 if (url != null) {
 2594  0
                     long lastModified = 0;
 2595  0
                     URLConnection connection = url.openConnection();
 2596  0
                     if (connection != null) {
 2597  
                         // disable caches to get the correct data
 2598  0
                         connection.setUseCaches(false);
 2599  0
                         if (connection instanceof JarURLConnection) {
 2600  0
                             JarEntry ent = ((JarURLConnection)connection).getJarEntry();
 2601  0
                             if (ent != null) {
 2602  0
                                 lastModified = ent.getTime();
 2603  0
                                 if (lastModified == -1) {
 2604  0
                                     lastModified = 0;
 2605  
                                 }
 2606  
                             }
 2607  0
                         } else {
 2608  0
                             lastModified = connection.getLastModified();
 2609  
                         }
 2610  
                     }
 2611  0
                     result = lastModified >= loadTime;
 2612  
                 }
 2613  0
             } catch (NullPointerException npe) {
 2614  0
                 throw npe;
 2615  0
             } catch (Exception e) {
 2616  
                 // ignore other exceptions
 2617  0
             }
 2618  0
             return result;
 2619  
         }
 2620  
 
 2621  
         /**
 2622  
          * Converts the given <code>baseName</code> and <code>locale</code>
 2623  
          * to the bundle name. This method is called from the default
 2624  
          * implementation of the {@link #newBundle(String, Locale, String,
 2625  
          * ClassLoader, boolean) newBundle} and {@link #needsReload(String,
 2626  
          * Locale, String, ClassLoader, ResourceBundle, long) needsReload}
 2627  
          * methods.
 2628  
          *
 2629  
          * <p>This implementation returns the following value:
 2630  
          * <pre>
 2631  
          *     baseName + "_" + language + "_" + country + "_" + variant
 2632  
          * </pre>
 2633  
          * where <code>language</code>, <code>country</code> and
 2634  
          * <code>variant</code> are the language, country and variant values
 2635  
          * of <code>locale</code>, respectively. Final component values that
 2636  
          * are empty Strings are omitted along with the preceding '_'. If
 2637  
          * all of the values are empty strings, then <code>baseName</code>
 2638  
          * is returned.
 2639  
          *
 2640  
          * <p>For example, if <code>baseName</code> is
 2641  
          * <code>"baseName"</code> and <code>locale</code> is
 2642  
          * <code>Locale("ja",&nbsp;"",&nbsp;"XX")</code>, then
 2643  
          * <code>"baseName_ja_&thinsp;_XX"</code> is returned. If the given
 2644  
          * locale is <code>Locale("en")</code>, then
 2645  
          * <code>"baseName_en"</code> is returned.
 2646  
          *
 2647  
          * <p>Overriding this method allows applications to use different
 2648  
          * conventions in the organization and packaging of localized
 2649  
          * resources.
 2650  
          *
 2651  
          * @param baseName
 2652  
          *        the base name of the resource bundle, a fully
 2653  
          *        qualified class name
 2654  
          * @param locale
 2655  
          *        the locale for which a resource bundle should be
 2656  
          *        loaded
 2657  
          * @return the bundle name for the resource bundle
 2658  
          * @exception NullPointerException
 2659  
          *        if <code>baseName</code> or <code>locale</code>
 2660  
          *        is <code>null</code>
 2661  
          */
 2662  
         public String toBundleName(String baseName, Locale locale) {
 2663  27
             if (locale == ROOTLOCALE) {
 2664  0
                 return baseName;
 2665  
             }
 2666  
 
 2667  27
             String language = locale.getLanguage();
 2668  27
             String country = locale.getCountry();
 2669  27
             String variant = locale.getVariant();
 2670  
 
 2671  
             // it's safe to use '==' to compare strings here, as they are intern'ed.
 2672  27
             if (language == "" && country == "" && variant == "") {
 2673  0
                 return baseName;
 2674  
             }
 2675  
 
 2676  27
             StringBuilder sb = new StringBuilder(baseName);
 2677  27
             sb.append('_');
 2678  27
             if (variant != "") {
 2679  0
                 sb.append(language).append('_').append(country).append('_').append(variant);
 2680  27
             } else if (country != "") {
 2681  12
                 sb.append(language).append('_').append(country);
 2682  
             } else {
 2683  15
                 sb.append(language);
 2684  
             }
 2685  27
             return sb.toString();
 2686  
 
 2687  
         }
 2688  
 
 2689  
         /**
 2690  
          * Converts the given <code>bundleName</code> to the form required
 2691  
          * by the {@link ClassLoader#getResource ClassLoader.getResource}
 2692  
          * method by replacing all occurrences of <code>'.'</code> in
 2693  
          * <code>bundleName</code> with <code>'/'</code> and appending a
 2694  
          * <code>'.'</code> and the given file <code>suffix</code>. For
 2695  
          * example, if <code>bundleName</code> is
 2696  
          * <code>"foo.bar.MyResources_ja_JP"</code> and <code>suffix</code>
 2697  
          * is <code>"properties"</code>, then
 2698  
          * <code>"foo/bar/MyResources_ja_JP.properties"</code> is returned.
 2699  
          *
 2700  
          * @param bundleName
 2701  
          *        the bundle name
 2702  
          * @param suffix
 2703  
          *        the file type suffix
 2704  
          * @return the converted resource name
 2705  
          * @exception NullPointerException
 2706  
          *         if <code>bundleName</code> or <code>suffix</code>
 2707  
          *         is <code>null</code>
 2708  
          */
 2709  
         public final String toResourceName(String bundleName, String suffix) {
 2710  27
             StringBuilder sb = new StringBuilder(bundleName.length() + 1 + suffix.length());
 2711  27
             sb.append(bundleName.replace('.', '/')).append('.').append(suffix);
 2712  27
             return sb.toString();
 2713  
         }
 2714  
     }
 2715  
 
 2716  0
     private static class SingleFormatControl extends Control {
 2717  0
         private static final Control PROPERTIES_ONLY
 2718  
             = new SingleFormatControl(FORMAT_PROPERTIES);
 2719  
 
 2720  0
         private static final Control CLASS_ONLY
 2721  
             = new SingleFormatControl(FORMAT_CLASS);
 2722  
 
 2723  
         private final List<String> formats;
 2724  
 
 2725  0
         protected SingleFormatControl(List<String> formats) {
 2726  0
             this.formats = formats;
 2727  0
         }
 2728  
 
 2729  
         @Override
 2730  
         public List<String> getFormats(String baseName) {
 2731  0
             if (baseName == null) {
 2732  0
                 throw new NullPointerException();
 2733  
             }
 2734  0
             return formats;
 2735  
         }
 2736  
     }
 2737  
 
 2738  0
     private static final class NoFallbackControl extends SingleFormatControl {
 2739  0
         private static final Control NO_FALLBACK
 2740  
             = new NoFallbackControl(FORMAT_DEFAULT);
 2741  
 
 2742  0
         private static final Control PROPERTIES_ONLY_NO_FALLBACK
 2743  
             = new NoFallbackControl(FORMAT_PROPERTIES);
 2744  
 
 2745  0
         private static final Control CLASS_ONLY_NO_FALLBACK
 2746  
             = new NoFallbackControl(FORMAT_CLASS);
 2747  
 
 2748  
         protected NoFallbackControl(List<String> formats) {
 2749  0
             super(formats);
 2750  0
         }
 2751  
 
 2752  
         @Override
 2753  
         public Locale getFallbackLocale(String baseName, Locale locale) {
 2754  0
             if (baseName == null || locale == null) {
 2755  0
                 throw new NullPointerException();
 2756  
             }
 2757  0
             return null;
 2758  
         }
 2759  
     }
 2760  
 }