Source Code Cross Referenced for ResourceBundle.java in  » 6.0-JDK-Core » Collections-Jar-Zip-Logging-regex » java » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » Collections Jar Zip Logging regex » java.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        /*
0027         * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
0028         * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
0029         *
0030         * The original version of this source code and documentation
0031         * is copyrighted and owned by Taligent, Inc., a wholly-owned
0032         * subsidiary of IBM. These materials are provided under terms
0033         * of a License Agreement between Taligent and Sun. This technology
0034         * is protected by multiple US and International patents.
0035         *
0036         * This notice and attribution to Taligent may not be removed.
0037         * Taligent is a registered trademark of Taligent, Inc.
0038         *
0039         */
0040
0041        package java.util;
0042
0043        import java.io.IOException;
0044        import java.io.InputStream;
0045        import java.lang.ref.ReferenceQueue;
0046        import java.lang.ref.SoftReference;
0047        import java.lang.ref.WeakReference;
0048        import java.net.JarURLConnection;
0049        import java.net.URL;
0050        import java.net.URLConnection;
0051        import java.security.AccessController;
0052        import java.security.PrivilegedAction;
0053        import java.security.PrivilegedActionException;
0054        import java.security.PrivilegedExceptionAction;
0055        import java.util.concurrent.ConcurrentHashMap;
0056        import java.util.concurrent.ConcurrentMap;
0057        import java.util.jar.JarEntry;
0058
0059        /**
0060         *
0061         * Resource bundles contain locale-specific objects.
0062         * When your program needs a locale-specific resource,
0063         * a <code>String</code> for example, your program can load it
0064         * from the resource bundle that is appropriate for the
0065         * current user's locale. In this way, you can write
0066         * program code that is largely independent of the user's
0067         * locale isolating most, if not all, of the locale-specific
0068         * information in resource bundles.
0069         *
0070         * <p>
0071         * This allows you to write programs that can:
0072         * <UL type=SQUARE>
0073         * <LI> be easily localized, or translated, into different languages
0074         * <LI> handle multiple locales at once
0075         * <LI> be easily modified later to support even more locales
0076         * </UL>
0077         *
0078         * <P>
0079         * Resource bundles belong to families whose members share a common base 
0080         * name, but whose names also have additional components that identify 
0081         * their locales. For example, the base name of a family of resource 
0082         * bundles might be "MyResources". The family should have a default 
0083         * resource bundle which simply has the same name as its family - 
0084         * "MyResources" - and will be used as the bundle of last resort if a
0085         * specific locale is not supported. The family can then provide as
0086         * many locale-specific members as needed, for example a German one
0087         * named "MyResources_de".
0088         *
0089         * <P>
0090         * Each resource bundle in a family contains the same items, but the items have
0091         * been translated for the locale represented by that resource bundle.
0092         * For example, both "MyResources" and "MyResources_de" may have a
0093         * <code>String</code> that's used on a button for canceling operations.
0094         * In "MyResources" the <code>String</code> may contain "Cancel" and in
0095         * "MyResources_de" it may contain "Abbrechen".
0096         *
0097         * <P>
0098         * If there are different resources for different countries, you
0099         * can make specializations: for example, "MyResources_de_CH" contains objects for
0100         * the German language (de) in Switzerland (CH). If you want to only
0101         * modify some of the resources
0102         * in the specialization, you can do so.
0103         *
0104         * <P>
0105         * When your program needs a locale-specific object, it loads
0106         * the <code>ResourceBundle</code> class using the
0107         * {@link #getBundle(java.lang.String, java.util.Locale) getBundle}
0108         * method:
0109         * <blockquote>
0110         * <pre>
0111         * ResourceBundle myResources =
0112         *      ResourceBundle.getBundle("MyResources", currentLocale);
0113         * </pre>
0114         * </blockquote>
0115         *
0116         * <P>
0117         * Resource bundles contain key/value pairs. The keys uniquely
0118         * identify a locale-specific object in the bundle. Here's an
0119         * example of a <code>ListResourceBundle</code> that contains
0120         * two key/value pairs:
0121         * <blockquote>
0122         * <pre>
0123         * public class MyResources extends ListResourceBundle {
0124         *     protected Object[][] getContents() {
0125         *         return new Object[][] {
0126         *             // LOCALIZE THE SECOND STRING OF EACH ARRAY (e.g., "OK")
0127         *             {"OkKey", "OK"},
0128         *             {"CancelKey", "Cancel"},
0129         *             // END OF MATERIAL TO LOCALIZE
0130         *        };
0131         *     }
0132         * }
0133         * </pre>
0134         * </blockquote>
0135         * Keys are always <code>String</code>s.
0136         * In this example, the keys are "OkKey" and "CancelKey".
0137         * In the above example, the values
0138         * are also <code>String</code>s--"OK" and "Cancel"--but
0139         * they don't have to be. The values can be any type of object.
0140         *
0141         * <P>
0142         * You retrieve an object from resource bundle using the appropriate
0143         * getter method. Because "OkKey" and "CancelKey"
0144         * are both strings, you would use <code>getString</code> to retrieve them:
0145         * <blockquote>
0146         * <pre>
0147         * button1 = new Button(myResources.getString("OkKey"));
0148         * button2 = new Button(myResources.getString("CancelKey"));
0149         * </pre>
0150         * </blockquote>
0151         * The getter methods all require the key as an argument and return
0152         * the object if found. If the object is not found, the getter method
0153         * throws a <code>MissingResourceException</code>.
0154         *
0155         * <P>
0156         * Besides <code>getString</code>, <code>ResourceBundle</code> also provides
0157         * a method for getting string arrays, <code>getStringArray</code>,
0158         * as well as a generic <code>getObject</code> method for any other
0159         * type of object. When using <code>getObject</code>, you'll
0160         * have to cast the result to the appropriate type. For example:
0161         * <blockquote>
0162         * <pre>
0163         * int[] myIntegers = (int[]) myResources.getObject("intList");
0164         * </pre>
0165         * </blockquote>
0166         *
0167         * <P>
0168         * The Java Platform provides two subclasses of <code>ResourceBundle</code>,
0169         * <code>ListResourceBundle</code> and <code>PropertyResourceBundle</code>,
0170         * that provide a fairly simple way to create resources.
0171         * As you saw briefly in a previous example, <code>ListResourceBundle</code>
0172         * manages its resource as a list of key/value pairs.
0173         * <code>PropertyResourceBundle</code> uses a properties file to manage
0174         * its resources.
0175         *
0176         * <p>
0177         * If <code>ListResourceBundle</code> or <code>PropertyResourceBundle</code>
0178         * do not suit your needs, you can write your own <code>ResourceBundle</code>
0179         * subclass.  Your subclasses must override two methods: <code>handleGetObject</code>
0180         * and <code>getKeys()</code>.
0181         *
0182         * <h4>ResourceBundle.Control</h4>
0183         *
0184         * The {@link ResourceBundle.Control} class provides information necessary
0185         * to perform the bundle loading process by the <code>getBundle</code>
0186         * factory methods that take a <code>ResourceBundle.Control</code>
0187         * instance. You can implement your own subclass in order to enable
0188         * non-standard resource bundle formats, change the search strategy, or
0189         * define caching parameters. Refer to the descriptions of the class and the
0190         * {@link #getBundle(String, Locale, ClassLoader, Control) getBundle}
0191         * factory method for details.
0192         *
0193         * <h4>Cache Management</h4>
0194         *
0195         * Resource bundle instances created by the <code>getBundle</code> factory
0196         * methods are cached by default, and the factory methods return the same
0197         * resource bundle instance multiple times if it has been
0198         * cached. <code>getBundle</code> clients may clear the cache, manage the
0199         * lifetime of cached resource bundle instances using time-to-live values,
0200         * or specify not to cache resource bundle instances. Refer to the
0201         * descriptions of the {@linkplain #getBundle(String, Locale, ClassLoader,
0202         * Control) <code>getBundle</code> factory method}, {@link
0203         * #clearCache(ClassLoader) clearCache}, {@link
0204         * Control#getTimeToLive(String, Locale)
0205         * ResourceBundle.Control.getTimeToLive}, and {@link
0206         * Control#needsReload(String, Locale, String, ClassLoader, ResourceBundle,
0207         * long) ResourceBundle.Control.needsReload} for details.
0208         *
0209         * <h4>Example</h4>
0210         *
0211         * The following is a very simple example of a <code>ResourceBundle</code>
0212         * subclass, <code>MyResources</code>, that manages two resources (for a larger number of
0213         * resources you would probably use a <code>Map</code>).
0214         * Notice that you don't need to supply a value if 
0215         * a "parent-level" <code>ResourceBundle</code> handles the same
0216         * key with the same value (as for the okKey below).
0217         * <blockquote>
0218         * <pre>
0219         * // default (English language, United States)
0220         * public class MyResources extends ResourceBundle {
0221         *     public Object handleGetObject(String key) {
0222         *         if (key.equals("okKey")) return "Ok";
0223         *         if (key.equals("cancelKey")) return "Cancel";
0224         *         return null;
0225         *     }
0226         * 
0227         *     public Enumeration&lt;String&gt; getKeys() {
0228         *         return Collections.enumeration(keySet());
0229         *     }
0230         * 
0231         *     // Overrides handleKeySet() so that the getKeys() implementation
0232         *     // can rely on the keySet() value.
0233         *     protected Set&lt;String&gt; handleKeySet() {
0234         *         return new HashSet&lt;String&gt;(Arrays.asList("okKey", "cancelKey"));
0235         *     }
0236         * }
0237         *
0238         * // German language
0239         * public class MyResources_de extends MyResources {
0240         *     public Object handleGetObject(String key) {
0241         *         // don't need okKey, since parent level handles it.
0242         *         if (key.equals("cancelKey")) return "Abbrechen";
0243         *         return null;
0244         *     }
0245         * 
0246         *     protected Set&lt;String&gt; handleKeySet() {
0247         *         return new HashSet&lt;String&gt;(Arrays.asList("cancelKey"));
0248         *     }
0249         * }
0250         * </pre>
0251         * </blockquote>
0252         * You do not have to restrict yourself to using a single family of
0253         * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
0254         * exception messages, <code>ExceptionResources</code>
0255         * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
0256         * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
0257         * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
0258         *
0259         * @see ListResourceBundle
0260         * @see PropertyResourceBundle
0261         * @see MissingResourceException
0262         * @since JDK1.1
0263         */
0264        public abstract class ResourceBundle {
0265
0266            /** initial size of the bundle cache */
0267            private static final int INITIAL_CACHE_SIZE = 32;
0268
0269            /** constant indicating that no resource bundle exists */
0270            private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {
0271                public Enumeration<String> getKeys() {
0272                    return null;
0273                }
0274
0275                protected Object handleGetObject(String key) {
0276                    return null;
0277                }
0278
0279                public String toString() {
0280                    return "NONEXISTENT_BUNDLE";
0281                }
0282            };
0283
0284            /**
0285             * The cache is a map from cache keys (with bundle base name, locale, and
0286             * class loader) to either a resource bundle or NONEXISTENT_BUNDLE wrapped by a
0287             * BundleReference.
0288             *
0289             * The cache is a ConcurrentMap, allowing the cache to be searched
0290             * concurrently by multiple threads.  This will also allow the cache keys
0291             * to be reclaimed along with the ClassLoaders they reference.
0292             *
0293             * This variable would be better named "cache", but we keep the old
0294             * name for compatibility with some workarounds for bug 4212439.
0295             */
0296            private static final ConcurrentMap<CacheKey, BundleReference> cacheList = new ConcurrentHashMap<CacheKey, BundleReference>(
0297                    INITIAL_CACHE_SIZE);
0298
0299            /**
0300             * This ConcurrentMap is used to keep multiple threads from loading the
0301             * same bundle concurrently.  The table entries are <CacheKey, Thread>
0302             * where CacheKey is the key for the bundle that is under construction
0303             * and Thread is the thread that is constructing the bundle.
0304             * This list is manipulated in findBundleInCache and putBundleInCache.
0305             */
0306            private static final ConcurrentMap<CacheKey, Thread> underConstruction = new ConcurrentHashMap<CacheKey, Thread>();
0307
0308            /**
0309             * Queue for reference objects referring to class loaders or bundles.
0310             */
0311            private static final ReferenceQueue referenceQueue = new ReferenceQueue();
0312
0313            /**
0314             * The parent bundle of this bundle.
0315             * The parent bundle is searched by {@link #getObject getObject}
0316             * when this bundle does not contain a particular resource.
0317             */
0318            protected ResourceBundle parent = null;
0319
0320            /**
0321             * The locale for this bundle.
0322             */
0323            private Locale locale = null;
0324
0325            /**
0326             * The base bundle name for this bundle.
0327             */
0328            private String name;
0329
0330            /**
0331             * The flag indicating this bundle has expired in the cache.
0332             */
0333            private volatile boolean expired;
0334
0335            /**
0336             * The back link to the cache key. null if this bundle isn't in
0337             * the cache (yet) or has expired.
0338             */
0339            private volatile CacheKey cacheKey;
0340
0341            /**
0342             * A Set of the keys contained only in this ResourceBundle.
0343             */
0344            private volatile Set<String> keySet;
0345
0346            /**
0347             * Sole constructor.  (For invocation by subclass constructors, typically
0348             * implicit.)
0349             */
0350            public ResourceBundle() {
0351            }
0352
0353            /**
0354             * Gets a string for the given key from this resource bundle or one of its parents.
0355             * Calling this method is equivalent to calling
0356             * <blockquote>
0357             * <code>(String) {@link #getObject(java.lang.String) getObject}(key)</code>.
0358             * </blockquote>
0359             *
0360             * @param key the key for the desired string
0361             * @exception NullPointerException if <code>key</code> is <code>null</code>
0362             * @exception MissingResourceException if no object for the given key can be found
0363             * @exception ClassCastException if the object found for the given key is not a string
0364             * @return the string for the given key
0365             */
0366            public final String getString(String key) {
0367                return (String) getObject(key);
0368            }
0369
0370            /**
0371             * Gets a string array for the given key from this resource bundle or one of its parents.
0372             * Calling this method is equivalent to calling
0373             * <blockquote>
0374             * <code>(String[]) {@link #getObject(java.lang.String) getObject}(key)</code>.
0375             * </blockquote>
0376             *
0377             * @param key the key for the desired string array
0378             * @exception NullPointerException if <code>key</code> is <code>null</code>
0379             * @exception MissingResourceException if no object for the given key can be found
0380             * @exception ClassCastException if the object found for the given key is not a string array
0381             * @return the string array for the given key
0382             */
0383            public final String[] getStringArray(String key) {
0384                return (String[]) getObject(key);
0385            }
0386
0387            /**
0388             * Gets an object for the given key from this resource bundle or one of its parents.
0389             * This method first tries to obtain the object from this resource bundle using
0390             * {@link #handleGetObject(java.lang.String) handleGetObject}.
0391             * If not successful, and the parent resource bundle is not null,
0392             * it calls the parent's <code>getObject</code> method.
0393             * If still not successful, it throws a MissingResourceException.
0394             *
0395             * @param key the key for the desired object
0396             * @exception NullPointerException if <code>key</code> is <code>null</code>
0397             * @exception MissingResourceException if no object for the given key can be found
0398             * @return the object for the given key
0399             */
0400            public final Object getObject(String key) {
0401                Object obj = handleGetObject(key);
0402                if (obj == null) {
0403                    if (parent != null) {
0404                        obj = parent.getObject(key);
0405                    }
0406                    if (obj == null)
0407                        throw new MissingResourceException(
0408                                "Can't find resource for bundle "
0409                                        + this .getClass().getName() + ", key "
0410                                        + key, this .getClass().getName(), key);
0411                }
0412                return obj;
0413            }
0414
0415            /**
0416             * Returns the locale of this resource bundle. This method can be used after a
0417             * call to getBundle() to determine whether the resource bundle returned really
0418             * corresponds to the requested locale or is a fallback.
0419             *
0420             * @return the locale of this resource bundle
0421             */
0422            public Locale getLocale() {
0423                return locale;
0424            }
0425
0426            /*
0427             * Automatic determination of the ClassLoader to be used to load
0428             * resources on behalf of the client.  N.B. The client is getLoader's
0429             * caller's caller.
0430             */
0431            private static ClassLoader getLoader() {
0432                Class[] stack = getClassContext();
0433                /* Magic number 2 identifies our caller's caller */
0434                Class c = stack[2];
0435                ClassLoader cl = (c == null) ? null : c.getClassLoader();
0436                if (cl == null) {
0437                    // When the caller's loader is the boot class loader, cl is null
0438                    // here. In that case, ClassLoader.getSystemClassLoader() may
0439                    // return the same class loader that the application is
0440                    // using. We therefore use a wrapper ClassLoader to create a
0441                    // separate scope for bundles loaded on behalf of the Java
0442                    // runtime so that these bundles cannot be returned from the
0443                    // cache to the application (5048280).
0444                    cl = RBClassLoader.INSTANCE;
0445                }
0446                return cl;
0447            }
0448
0449            private static native Class[] getClassContext();
0450
0451            /**
0452             * A wrapper of ClassLoader.getSystemClassLoader().
0453             */
0454            private static class RBClassLoader extends ClassLoader {
0455                private static final RBClassLoader INSTANCE = AccessController
0456                        .doPrivileged(new PrivilegedAction<RBClassLoader>() {
0457                            public RBClassLoader run() {
0458                                return new RBClassLoader();
0459                            }
0460                        });
0461                private static final ClassLoader loader = ClassLoader
0462                        .getSystemClassLoader();
0463
0464                private RBClassLoader() {
0465                }
0466
0467                public Class<?> loadClass(String name)
0468                        throws ClassNotFoundException {
0469                    if (loader != null) {
0470                        return loader.loadClass(name);
0471                    }
0472                    return Class.forName(name);
0473                }
0474
0475                public URL getResource(String name) {
0476                    if (loader != null) {
0477                        return loader.getResource(name);
0478                    }
0479                    return ClassLoader.getSystemResource(name);
0480                }
0481
0482                public InputStream getResourceAsStream(String name) {
0483                    if (loader != null) {
0484                        return loader.getResourceAsStream(name);
0485                    }
0486                    return ClassLoader.getSystemResourceAsStream(name);
0487                }
0488            }
0489
0490            /**
0491             * Sets the parent bundle of this bundle.
0492             * The parent bundle is searched by {@link #getObject getObject}
0493             * when this bundle does not contain a particular resource.
0494             *
0495             * @param parent this bundle's parent bundle.
0496             */
0497            protected void setParent(ResourceBundle parent) {
0498                assert parent != NONEXISTENT_BUNDLE;
0499                this .parent = parent;
0500            }
0501
0502            /**
0503             * Key used for cached resource bundles.  The key checks the base
0504             * name, the locale, and the class loader to determine if the
0505             * resource is a match to the requested one. The loader may be
0506             * null, but the base name and the locale must have a non-null
0507             * value.
0508             */
0509            private static final class CacheKey implements  Cloneable {
0510                // These three are the actual keys for lookup in Map.
0511                private String name;
0512                private Locale locale;
0513                private LoaderReference loaderRef;
0514
0515                // bundle format which is necessary for calling
0516                // Control.needsReload().
0517                private String format;
0518
0519                // These time values are in CacheKey so that NONEXISTENT_BUNDLE
0520                // doesn't need to be cloned for caching.
0521
0522                // The time when the bundle has been loaded
0523                private volatile long loadTime;
0524
0525                // The time when the bundle expires in the cache, or either
0526                // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
0527                private volatile long expirationTime;
0528
0529                // Placeholder for an error report by a Throwable
0530                private Throwable cause;
0531
0532                // Hash code value cache to avoid recalculating the hash code
0533                // of this instance.
0534                private int hashCodeCache;
0535
0536                CacheKey(String baseName, Locale locale, ClassLoader loader) {
0537                    this .name = baseName;
0538                    this .locale = locale;
0539                    if (loader == null) {
0540                        this .loaderRef = null;
0541                    } else {
0542                        loaderRef = new LoaderReference(loader, referenceQueue,
0543                                this );
0544                    }
0545                    calculateHashCode();
0546                }
0547
0548                String getName() {
0549                    return name;
0550                }
0551
0552                CacheKey setName(String baseName) {
0553                    if (!this .name.equals(baseName)) {
0554                        this .name = baseName;
0555                        calculateHashCode();
0556                    }
0557                    return this ;
0558                }
0559
0560                Locale getLocale() {
0561                    return locale;
0562                }
0563
0564                CacheKey setLocale(Locale locale) {
0565                    if (!this .locale.equals(locale)) {
0566                        this .locale = locale;
0567                        calculateHashCode();
0568                    }
0569                    return this ;
0570                }
0571
0572                ClassLoader getLoader() {
0573                    return (loaderRef != null) ? loaderRef.get() : null;
0574                }
0575
0576                public boolean equals(Object other) {
0577                    if (this  == other) {
0578                        return true;
0579                    }
0580                    try {
0581                        final CacheKey otherEntry = (CacheKey) other;
0582                        //quick check to see if they are not equal
0583                        if (hashCodeCache != otherEntry.hashCodeCache) {
0584                            return false;
0585                        }
0586                        //are the names the same?
0587                        if (!name.equals(otherEntry.name)) {
0588                            return false;
0589                        }
0590                        // are the locales the same?
0591                        if (!locale.equals(otherEntry.locale)) {
0592                            return false;
0593                        }
0594                        //are refs (both non-null) or (both null)?
0595                        if (loaderRef == null) {
0596                            return otherEntry.loaderRef == null;
0597                        }
0598                        ClassLoader loader = loaderRef.get();
0599                        return (otherEntry.loaderRef != null)
0600                                // with a null reference we can no longer find
0601                                // out which class loader was referenced; so
0602                                // treat it as unequal
0603                                && (loader != null)
0604                                && (loader == otherEntry.loaderRef.get());
0605                    } catch (NullPointerException e) {
0606                    } catch (ClassCastException e) {
0607                    }
0608                    return false;
0609                }
0610
0611                public int hashCode() {
0612                    return hashCodeCache;
0613                }
0614
0615                private void calculateHashCode() {
0616                    hashCodeCache = name.hashCode() << 3;
0617                    hashCodeCache ^= locale.hashCode();
0618                    ClassLoader loader = getLoader();
0619                    if (loader != null) {
0620                        hashCodeCache ^= loader.hashCode();
0621                    }
0622                }
0623
0624                public Object clone() {
0625                    try {
0626                        CacheKey clone = (CacheKey) super .clone();
0627                        if (loaderRef != null) {
0628                            clone.loaderRef = new LoaderReference(loaderRef
0629                                    .get(), referenceQueue, clone);
0630                        }
0631                        // Clear the reference to a Throwable
0632                        clone.cause = null;
0633                        return clone;
0634                    } catch (CloneNotSupportedException e) {
0635                        //this should never happen
0636                        throw new InternalError();
0637                    }
0638                }
0639
0640                String getFormat() {
0641                    return format;
0642                }
0643
0644                void setFormat(String format) {
0645                    this .format = format;
0646                }
0647
0648                private void setCause(Throwable cause) {
0649                    if (this .cause == null) {
0650                        this .cause = cause;
0651                    } else {
0652                        // Override the cause if the previous one is
0653                        // ClassNotFoundException.
0654                        if (this .cause instanceof  ClassNotFoundException) {
0655                            this .cause = cause;
0656                        }
0657                    }
0658                }
0659
0660                private Throwable getCause() {
0661                    return cause;
0662                }
0663
0664                public String toString() {
0665                    String l = locale.toString();
0666                    if (l.length() == 0) {
0667                        if (locale.getVariant().length() != 0) {
0668                            l = "__" + locale.getVariant();
0669                        } else {
0670                            l = "\"\"";
0671                        }
0672                    }
0673                    return "CacheKey[" + name + ", lc=" + l + ", ldr="
0674                            + getLoader() + "(format=" + format + ")]";
0675                }
0676            }
0677
0678            /**
0679             * The common interface to get a CacheKey in LoaderReference and
0680             * BundleReference.
0681             */
0682            private static interface CacheKeyReference {
0683                public CacheKey getCacheKey();
0684            }
0685
0686            /**
0687             * References to class loaders are weak references, so that they can be
0688             * garbage collected when nobody else is using them. The ResourceBundle
0689             * class has no reason to keep class loaders alive.
0690             */
0691            private static final class LoaderReference extends
0692                    WeakReference<ClassLoader> implements  CacheKeyReference {
0693                private CacheKey cacheKey;
0694
0695                LoaderReference(ClassLoader referent, ReferenceQueue q,
0696                        CacheKey key) {
0697                    super (referent, q);
0698                    cacheKey = key;
0699                }
0700
0701                public CacheKey getCacheKey() {
0702                    return cacheKey;
0703                }
0704            }
0705
0706            /**
0707             * References to bundles are soft references so that they can be garbage
0708             * collected when they have no hard references.
0709             */
0710            private static final class BundleReference extends
0711                    SoftReference<ResourceBundle> implements  CacheKeyReference {
0712                private CacheKey cacheKey;
0713
0714                BundleReference(ResourceBundle referent, ReferenceQueue q,
0715                        CacheKey key) {
0716                    super (referent, q);
0717                    cacheKey = key;
0718                }
0719
0720                public CacheKey getCacheKey() {
0721                    return cacheKey;
0722                }
0723            }
0724
0725            /**
0726             * Gets a resource bundle using the specified base name, the default locale,
0727             * and the caller's class loader. Calling this method is equivalent to calling
0728             * <blockquote>
0729             * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
0730             * </blockquote>
0731             * except that <code>getClassLoader()</code> is run with the security
0732             * privileges of <code>ResourceBundle</code>.
0733             * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
0734             * for a complete description of the search and instantiation strategy.
0735             *
0736             * @param baseName the base name of the resource bundle, a fully qualified class name
0737             * @exception java.lang.NullPointerException
0738             *     if <code>baseName</code> is <code>null</code>
0739             * @exception MissingResourceException
0740             *     if no resource bundle for the specified base name can be found
0741             * @return a resource bundle for the given base name and the default locale
0742             */
0743            public static final ResourceBundle getBundle(String baseName) {
0744                return getBundleImpl(baseName, Locale.getDefault(),
0745                /* must determine loader here, else we break stack invariant */
0746                getLoader(), Control.INSTANCE);
0747            }
0748
0749            /**
0750             * Returns a resource bundle using the specified base name, the
0751             * default locale and the specified control. Calling this method
0752             * is equivalent to calling
0753             * <pre>
0754             * getBundle(baseName, Locale.getDefault(),
0755             *           this.getClass().getClassLoader(), control),
0756             * </pre>
0757             * except that <code>getClassLoader()</code> is run with the security
0758             * privileges of <code>ResourceBundle</code>.  See {@link
0759             * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
0760             * complete description of the resource bundle loading process with a
0761             * <code>ResourceBundle.Control</code>.
0762             *
0763             * @param baseName
0764             *        the base name of the resource bundle, a fully qualified class
0765             *        name
0766             * @param control
0767             *        the control which gives information for the resource bundle
0768             *        loading process
0769             * @return a resource bundle for the given base name and the default
0770             *        locale
0771             * @exception NullPointerException
0772             *        if <code>baseName</code> or <code>control</code> is
0773             *        <code>null</code>
0774             * @exception MissingResourceException
0775             *        if no resource bundle for the specified base name can be found
0776             * @exception IllegalArgumentException
0777             *        if the given <code>control</code> doesn't perform properly
0778             *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
0779             *        Note that validation of <code>control</code> is performed as
0780             *        needed.
0781             * @since 1.6
0782             */
0783            public static final ResourceBundle getBundle(String baseName,
0784                    Control control) {
0785                return getBundleImpl(baseName, Locale.getDefault(),
0786                /* must determine loader here, else we break stack invariant */
0787                getLoader(), control);
0788            }
0789
0790            /**
0791             * Gets a resource bundle using the specified base name and locale,
0792             * and the caller's class loader. Calling this method is equivalent to calling
0793             * <blockquote>
0794             * <code>getBundle(baseName, locale, this.getClass().getClassLoader())</code>,
0795             * </blockquote>
0796             * except that <code>getClassLoader()</code> is run with the security
0797             * privileges of <code>ResourceBundle</code>.
0798             * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
0799             * for a complete description of the search and instantiation strategy.
0800             *
0801             * @param baseName
0802             *        the base name of the resource bundle, a fully qualified class name
0803             * @param locale
0804             *        the locale for which a resource bundle is desired
0805             * @exception NullPointerException
0806             *        if <code>baseName</code> or <code>locale</code> is <code>null</code>
0807             * @exception MissingResourceException
0808             *        if no resource bundle for the specified base name can be found
0809             * @return a resource bundle for the given base name and locale
0810             */
0811            public static final ResourceBundle getBundle(String baseName,
0812                    Locale locale) {
0813                return getBundleImpl(baseName, locale,
0814                /* must determine loader here, else we break stack invariant */
0815                getLoader(), Control.INSTANCE);
0816            }
0817
0818            /**
0819             * Returns a resource bundle using the specified base name, target
0820             * locale and control, and the caller's class loader. Calling this
0821             * method is equivalent to calling
0822             * <pre>
0823             * getBundle(baseName, targetLocale, this.getClass().getClassLoader(),
0824             *           control),
0825             * </pre>
0826             * except that <code>getClassLoader()</code> is run with the security
0827             * privileges of <code>ResourceBundle</code>.  See {@link
0828             * #getBundle(String, Locale, ClassLoader, Control) getBundle} for the
0829             * complete description of the resource bundle loading process with a
0830             * <code>ResourceBundle.Control</code>.
0831             *
0832             * @param baseName
0833             *        the base name of the resource bundle, a fully qualified
0834             *        class name
0835             * @param targetLocale
0836             *        the locale for which a resource bundle is desired
0837             * @param control
0838             *        the control which gives information for the resource
0839             *        bundle loading process
0840             * @return a resource bundle for the given base name and a
0841             *        <code>Locale</code> in <code>locales</code>
0842             * @exception NullPointerException
0843             *        if <code>baseName</code>, <code>locales</code> or
0844             *        <code>control</code> is <code>null</code>
0845             * @exception MissingResourceException
0846             *        if no resource bundle for the specified base name in any
0847             *        of the <code>locales</code> can be found.
0848             * @exception IllegalArgumentException
0849             *        if the given <code>control</code> doesn't perform properly
0850             *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
0851             *        Note that validation of <code>control</code> is performed as
0852             *        needed.
0853             * @since 1.6
0854             */
0855            public static final ResourceBundle getBundle(String baseName,
0856                    Locale targetLocale, Control control) {
0857                return getBundleImpl(baseName, targetLocale,
0858                /* must determine loader here, else we break stack invariant */
0859                getLoader(), control);
0860            }
0861
0862            /**
0863             * Gets a resource bundle using the specified base name, locale, and class loader.
0864             *
0865             * <p><a name="default_behavior"/>
0866             * Conceptually, <code>getBundle</code> uses the following strategy for locating and instantiating
0867             * resource bundles:
0868             * <p>
0869             * <code>getBundle</code> uses the base name, the specified locale, and the default
0870             * locale (obtained from {@link java.util.Locale#getDefault() Locale.getDefault})
0871             * to generate a sequence of <a name="candidates"><em>candidate bundle names</em></a>.
0872             * If the specified locale's language, country, and variant are all empty
0873             * strings, then the base name is the only candidate bundle name.
0874             * Otherwise, the following sequence is generated from the attribute
0875             * values of the specified locale (language1, country1, and variant1)
0876             * and of the default locale (language2, country2, and variant2):
0877             * <ul>
0878             * <li> baseName + "_" + language1 + "_" + country1 + "_" + variant1
0879             * <li> baseName + "_" + language1 + "_" + country1
0880             * <li> baseName + "_" + language1
0881             * <li> baseName + "_" + language2 + "_" + country2 + "_" + variant2
0882             * <li> baseName + "_" + language2 + "_" + country2
0883             * <li> baseName + "_" + language2
0884             * <li> baseName
0885             * </ul>
0886             * <p>
0887             * Candidate bundle names where the final component is an empty string are omitted.
0888             * For example, if country1 is an empty string, the second candidate bundle name is omitted.
0889             *
0890             * <p>
0891             * <code>getBundle</code> then iterates over the candidate bundle names to find the first
0892             * one for which it can <em>instantiate</em> an actual resource bundle. For each candidate
0893             * bundle name, it attempts to create a resource bundle:
0894             * <ul>
0895             * <li>
0896             * First, it attempts to load a class using the candidate bundle name.
0897             * If such a class can be found and loaded using the specified class loader, is assignment
0898             * compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated,
0899             * <code>getBundle</code> creates a new instance of this class and uses it as the <em>result
0900             * resource bundle</em>.
0901             * <li>
0902             * Otherwise, <code>getBundle</code> attempts to locate a property resource file.
0903             * It generates a path name from the candidate bundle name by replacing all "." characters
0904             * with "/" and appending the string ".properties".
0905             * It attempts to find a "resource" with this name using
0906             * {@link java.lang.ClassLoader#getResource(java.lang.String) ClassLoader.getResource}.
0907             * (Note that a "resource" in the sense of <code>getResource</code> has nothing to do with
0908             * the contents of a resource bundle, it is just a container of data, such as a file.)
0909             * If it finds a "resource", it attempts to create a new
0910             * {@link PropertyResourceBundle} instance from its contents.
0911             * If successful, this instance becomes the <em>result resource bundle</em>.
0912             * </ul>
0913             *
0914             * <p>
0915             * If no result resource bundle has been found, a <code>MissingResourceException</code>
0916             * is thrown.
0917             *
0918             * <p><a name="parent_chain"/>
0919             * Once a result resource bundle has been found, its <em>parent chain</em> is instantiated.
0920             * <code>getBundle</code> iterates over the candidate bundle names that can be
0921             * obtained by successively removing variant, country, and language
0922             * (each time with the preceding "_") from the bundle name of the result resource bundle.
0923             * As above, candidate bundle names where the final component is an empty string are omitted.
0924             * With each of the candidate bundle names it attempts to instantiate a resource bundle, as
0925             * described above.
0926             * Whenever it succeeds, it calls the previously instantiated resource
0927             * bundle's {@link #setParent(java.util.ResourceBundle) setParent} method
0928             * with the new resource bundle, unless the previously instantiated resource
0929             * bundle already has a non-null parent.
0930             *
0931             * <p>
0932             * <code>getBundle</code> caches instantiated resource bundles and
0933             * may return the same resource bundle instance multiple
0934             * times.
0935             *
0936             * <p>
0937             * The <code>baseName</code> argument should be a fully qualified class name. However, for
0938             * compatibility with earlier versions, Sun's Java SE Runtime Environments do not verify this,
0939             * and so it is possible to access <code>PropertyResourceBundle</code>s by specifying a
0940             * path name (using "/") instead of a fully qualified class name (using ".").
0941             *
0942             * <p><a name="default_behavior_example"/>
0943             * <strong>Example:</strong><br>The following class and property files are provided:
0944             * <pre>
0945             *     MyResources.class
0946             *     MyResources.properties
0947             *     MyResources_fr.properties
0948             *     MyResources_fr_CH.class
0949             *     MyResources_fr_CH.properties
0950             *     MyResources_en.properties
0951             *     MyResources_es_ES.class
0952             * </pre>
0953             * The contents of all files are valid (that is, public non-abstract subclasses of <code>ResourceBundle</code> for
0954             * the ".class" files, syntactically correct ".properties" files).
0955             * The default locale is <code>Locale("en", "GB")</code>.
0956             * <p>
0957             * Calling <code>getBundle</code> with the shown locale argument values instantiates
0958             * resource bundles from the following sources:
0959             * <ul>
0960             * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class
0961             * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent MyResources.class
0962             * <li>Locale("de", "DE"): result MyResources_en.properties, parent MyResources.class
0963             * <li>Locale("en", "US"): result MyResources_en.properties, parent MyResources.class
0964             * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent MyResources.class
0965             * </ul>
0966             * <p>The file MyResources_fr_CH.properties is never used because it is hidden by
0967             * MyResources_fr_CH.class. Likewise, MyResources.properties is also hidden by
0968             * MyResources.class.
0969             *
0970             * @param baseName the base name of the resource bundle, a fully qualified class name
0971             * @param locale the locale for which a resource bundle is desired
0972             * @param loader the class loader from which to load the resource bundle
0973             * @return a resource bundle for the given base name and locale
0974             * @exception java.lang.NullPointerException
0975             *        if <code>baseName</code>, <code>locale</code>, or <code>loader</code> is <code>null</code>
0976             * @exception MissingResourceException
0977             *        if no resource bundle for the specified base name can be found
0978             * @since 1.2
0979             */
0980            public static ResourceBundle getBundle(String baseName,
0981                    Locale locale, ClassLoader loader) {
0982                if (loader == null) {
0983                    throw new NullPointerException();
0984                }
0985                return getBundleImpl(baseName, locale, loader, Control.INSTANCE);
0986            }
0987
0988            /**
0989             * Returns a resource bundle using the specified base name, target
0990             * locale, class loader and control. Unlike the {@linkplain
0991             * #getBundle(String, Locale, ClassLoader) <code>getBundle</code>
0992             * factory methods with no <code>control</code> argument}, the given
0993             * <code>control</code> specifies how to locate and instantiate resource
0994             * bundles. Conceptually, the bundle loading process with the given
0995             * <code>control</code> is performed in the following steps.
0996             *
0997             * <p>
0998             * <ol>
0999             * <li>This factory method looks up the resource bundle in the cache for
1000             * the specified <code>baseName</code>, <code>targetLocale</code> and
1001             * <code>loader</code>.  If the requested resource bundle instance is
1002             * found in the cache and the time-to-live periods of the instance and
1003             * all of its parent instances have not expired, the instance is returned
1004             * to the caller. Otherwise, this factory method proceeds with the
1005             * loading process below.</li>
1006             *
1007             * <li>The {@link ResourceBundle.Control#getFormats(String)
1008             * control.getFormats} method is called to get resource bundle formats
1009             * to produce bundle or resource names. The strings
1010             * <code>"java.class"</code> and <code>"java.properties"</code>
1011             * designate class-based and {@linkplain PropertyResourceBundle
1012             * property}-based resource bundles, respectively. Other strings
1013             * starting with <code>"java."</code> are reserved for future extensions
1014             * and must not be used for application-defined formats. Other strings
1015             * designate application-defined formats.</li>
1016             *
1017             * <li>The {@link ResourceBundle.Control#getCandidateLocales(String,
1018             * Locale) control.getCandidateLocales} method is called with the target
1019             * locale to get a list of <em>candidate <code>Locale</code>s</em> for
1020             * which resource bundles are searched.</li>
1021             *
1022             * <li>The {@link ResourceBundle.Control#newBundle(String, Locale,
1023             * String, ClassLoader, boolean) control.newBundle} method is called to
1024             * instantiate a <code>ResourceBundle</code> for the base bundle name, a
1025             * candidate locale, and a format. (Refer to the note on the cache
1026             * lookup below.) This step is iterated over all combinations of the
1027             * candidate locales and formats until the <code>newBundle</code> method
1028             * returns a <code>ResourceBundle</code> instance or the iteration has
1029             * used up all the combinations. For example, if the candidate locales
1030             * are <code>Locale("de", "DE")</code>, <code>Locale("de")</code> and
1031             * <code>Locale("")</code> and the formats are <code>"java.class"</code>
1032             * and <code>"java.properties"</code>, then the following is the
1033             * sequence of locale-format combinations to be used to call
1034             * <code>control.newBundle</code>.
1035             *
1036             * <table style="width: 50%; text-align: left; margin-left: 40px;"
1037             *  border="0" cellpadding="2" cellspacing="2">
1038             * <tbody><code>
1039             * <tr>
1040             * <td
1041             * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">Locale<br>
1042             * </td>
1043             * <td
1044             * style="vertical-align: top; text-align: left; font-weight: bold; width: 50%;">format<br>
1045             * </td>
1046             * </tr>
1047             * <tr>
1048             * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")<br>
1049             * </td>
1050             * <td style="vertical-align: top; width: 50%;">java.class<br>
1051             * </td>
1052             * </tr>
1053             * <tr>
1054             * <td style="vertical-align: top; width: 50%;">Locale("de", "DE")</td>
1055             * <td style="vertical-align: top; width: 50%;">java.properties<br>
1056             * </td>
1057             * </tr>
1058             * <tr>
1059             * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
1060             * <td style="vertical-align: top; width: 50%;">java.class</td>
1061             * </tr>
1062             * <tr>
1063             * <td style="vertical-align: top; width: 50%;">Locale("de")</td>
1064             * <td style="vertical-align: top; width: 50%;">java.properties</td>
1065             * </tr>
1066             * <tr>
1067             * <td style="vertical-align: top; width: 50%;">Locale("")<br>
1068             * </td>
1069             * <td style="vertical-align: top; width: 50%;">java.class</td>
1070             * </tr>
1071             * <tr>
1072             * <td style="vertical-align: top; width: 50%;">Locale("")</td>
1073             * <td style="vertical-align: top; width: 50%;">java.properties</td>
1074             * </tr>
1075             * </code></tbody>
1076             * </table>
1077             * </li>
1078             *
1079             * <li>If the previous step has found no resource bundle, proceed to
1080             * Step 6. If a bundle has been found that is a base bundle (a bundle
1081             * for <code>Locale("")</code>), and the candidate locale list only contained
1082             * <code>Locale("")</code>, return the bundle to the caller. If a bundle
1083             * has been found that is a base bundle, but the candidate locale list
1084             * contained locales other than Locale(""), put the bundle on hold and
1085             * proceed to Step 6. If a bundle has been found that is not a base
1086             * bundle, proceed to Step 7.</li>
1087             *
1088             * <li>The {@link ResourceBundle.Control#getFallbackLocale(String,
1089             * Locale) control.getFallbackLocale} method is called to get a fallback
1090             * locale (alternative to the current target locale) to try further
1091             * finding a resource bundle. If the method returns a non-null locale,
1092             * it becomes the next target locale and the loading process starts over
1093             * from Step 3. Otherwise, if a base bundle was found and put on hold in
1094             * a previous Step 5, it is returned to the caller now. Otherwise, a
1095             * MissingResourceException is thrown.</li>
1096             *
1097             * <li>At this point, we have found a resource bundle that's not the
1098             * base bundle. If this bundle set its parent during its instantiation,
1099             * it is returned to the caller. Otherwise, its <a
1100             * href="./ResourceBundle.html#parent_chain">parent chain</a> is
1101             * instantiated based on the list of candidate locales from which it was
1102             * found. Finally, the bundle is returned to the caller.</li>
1103             *
1104             *
1105             * </ol>
1106             *
1107             * <p>During the resource bundle loading process above, this factory
1108             * method looks up the cache before calling the {@link
1109             * Control#newBundle(String, Locale, String, ClassLoader, boolean)
1110             * control.newBundle} method.  If the time-to-live period of the
1111             * resource bundle found in the cache has expired, the factory method
1112             * calls the {@link ResourceBundle.Control#needsReload(String, Locale,
1113             * String, ClassLoader, ResourceBundle, long) control.needsReload}
1114             * method to determine whether the resource bundle needs to be reloaded.
1115             * If reloading is required, the factory method calls
1116             * <code>control.newBundle</code> to reload the resource bundle.  If
1117             * <code>control.newBundle</code> returns <code>null</code>, the factory
1118             * method puts a dummy resource bundle in the cache as a mark of
1119             * nonexistent resource bundles in order to avoid lookup overhead for
1120             * subsequent requests. Such dummy resource bundles are under the same
1121             * expiration control as specified by <code>control</code>.
1122             *
1123             * <p>All resource bundles loaded are cached by default. Refer to
1124             * {@link Control#getTimeToLive(String,Locale)
1125             * control.getTimeToLive} for details.
1126             *
1127             *
1128             * <p>The following is an example of the bundle loading process with the
1129             * default <code>ResourceBundle.Control</code> implementation.
1130             * 
1131             * <p>Conditions:
1132             * <ul>
1133             * <li>Base bundle name: <code>foo.bar.Messages</code>
1134             * <li>Requested <code>Locale</code>: {@link Locale#ITALY}</li>
1135             * <li>Default <code>Locale</code>: {@link Locale#FRENCH}</li>
1136             * <li>Available resource bundles:
1137             * <code>foo/bar/Messages_fr.properties</code> and
1138             * <code>foo/bar/Messages.properties</code></li>
1139             *
1140             * </ul>
1141             *
1142             * <p>First, <code>getBundle</code> tries loading a resource bundle in
1143             * the following sequence.
1144             *
1145             * <ul>
1146             * <li>class <code>foo.bar.Messages_it_IT</code>
1147             * <li>file <code>foo/bar/Messages_it_IT.properties</code>
1148             * <li>class <code>foo.bar.Messages_it</code></li>
1149             * <li>file <code>foo/bar/Messages_it.properties</code></li>
1150             * <li>class <code>foo.bar.Messages</code></li>
1151             * <li>file <code>foo/bar/Messages.properties</code></li>
1152             * </ul>
1153             *
1154             * <p>At this point, <code>getBundle</code> finds
1155             * <code>foo/bar/Messages.properties</code>, which is put on hold
1156             * because it's the base bundle.  <code>getBundle</code> calls {@link
1157             * Control#getFallbackLocale(String, Locale)
1158             * control.getFallbackLocale("foo.bar.Messages", Locale.ITALY)} which
1159             * returns <code>Locale.FRENCH</code>. Next, <code>getBundle</code>
1160             * tries loading a bundle in the following sequence.
1161             *
1162             * <ul>
1163             * <li>class <code>foo.bar.Messages_fr</code></li>
1164             * <li>file <code>foo/bar/Messages_fr.properties</code></li>
1165             * <li>class <code>foo.bar.Messages</code></li>
1166             * <li>file <code>foo/bar/Messages.properties</code></li>
1167             * </ul>
1168             *
1169             * <p><code>getBundle</code> finds
1170             * <code>foo/bar/Messages_fr.properties</code> and creates a
1171             * <code>ResourceBundle</code> instance. Then, <code>getBundle</code>
1172             * sets up its parent chain from the list of the candiate locales.  Only
1173             * <code>foo/bar/Messages.properties</code> is found in the list and
1174             * <code>getBundle</code> creates a <code>ResourceBundle</code> instance
1175             * that becomes the parent of the instance for
1176             * <code>foo/bar/Messages_fr.properties</code>.
1177             *
1178             * @param baseName
1179             *        the base name of the resource bundle, a fully qualified
1180             *        class name
1181             * @param targetLocale
1182             *        the locale for which a resource bundle is desired
1183             * @param loader
1184             *        the class loader from which to load the resource bundle
1185             * @param control
1186             *        the control which gives information for the resource
1187             *        bundle loading process
1188             * @return a resource bundle for the given base name and locale
1189             * @exception NullPointerException
1190             *        if <code>baseName</code>, <code>targetLocale</code>,
1191             *        <code>loader</code>, or <code>control</code> is
1192             *        <code>null</code>
1193             * @exception MissingResourceException
1194             *        if no resource bundle for the specified base name can be found
1195             * @exception IllegalArgumentException
1196             *        if the given <code>control</code> doesn't perform properly
1197             *        (e.g., <code>control.getCandidateLocales</code> returns null.) 
1198             *        Note that validation of <code>control</code> is performed as
1199             *        needed.
1200             * @since 1.6
1201             */
1202            public static ResourceBundle getBundle(String baseName,
1203                    Locale targetLocale, ClassLoader loader, Control control) {
1204                if (loader == null || control == null) {
1205                    throw new NullPointerException();
1206                }
1207                return getBundleImpl(baseName, targetLocale, loader, control);
1208            }
1209
1210            private static ResourceBundle getBundleImpl(String baseName,
1211                    Locale locale, ClassLoader loader, Control control) {
1212                if (locale == null || control == null) {
1213                    throw new NullPointerException();
1214                }
1215
1216                // We create a CacheKey here for use by this call. The base
1217                // name and loader will never change during the bundle loading
1218                // process. We have to make sure that the locale is set before
1219                // using it as a cache key.
1220                CacheKey cacheKey = new CacheKey(baseName, locale, loader);
1221                ResourceBundle bundle = null;
1222
1223                // Quick lookup of the cache.
1224                BundleReference bundleRef = cacheList.get(cacheKey);
1225                if (bundleRef != null) {
1226                    bundle = bundleRef.get();
1227                    bundleRef = null;
1228                }
1229
1230                // If this bundle and all of its parents are valid (not expired),
1231                // then return this bundle. If any of the bundles is expired, we
1232                // don't call control.needsReload here but instead drop into the
1233                // complete loading process below.
1234                if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
1235                    return bundle;
1236                }
1237
1238                // No valid bundle was found in the cache, so we need to load the
1239                // resource bundle and its parents.
1240
1241                boolean isKnownControl = (control == Control.INSTANCE)
1242                        || (control instanceof  SingleFormatControl);
1243                List<String> formats = control.getFormats(baseName);
1244                if (!isKnownControl && !checkList(formats)) {
1245                    throw new IllegalArgumentException(
1246                            "Invalid Control: getFormats");
1247                }
1248
1249                ResourceBundle baseBundle = null;
1250                for (Locale targetLocale = locale; targetLocale != null; targetLocale = control
1251                        .getFallbackLocale(baseName, targetLocale)) {
1252                    List<Locale> candidateLocales = control
1253                            .getCandidateLocales(baseName, targetLocale);
1254                    if (!isKnownControl && !checkList(candidateLocales)) {
1255                        throw new IllegalArgumentException(
1256                                "Invalid Control: getCandidateLocales");
1257                    }
1258
1259                    bundle = findBundle(cacheKey, candidateLocales, formats, 0,
1260                            control, baseBundle);
1261
1262                    // If the loaded bundle is the base bundle and exactly for the
1263                    // requested locale or the only candidate locale, then take the
1264                    // bundle as the resulting one. If the loaded bundle is the base
1265                    // bundle, it's put on hold until we finish processing all
1266                    // fallback locales.
1267                    if (isValidBundle(bundle)) {
1268                        boolean isBaseBundle = Locale.ROOT
1269                                .equals(bundle.locale);
1270                        if (!isBaseBundle
1271                                || bundle.locale.equals(locale)
1272                                || (candidateLocales.size() == 1 && bundle.locale
1273                                        .equals(candidateLocales.get(0)))) {
1274                            break;
1275                        }
1276
1277                        // If the base bundle has been loaded, keep the reference in
1278                        // baseBundle so that we can avoid any redundant loading in case
1279                        // the control specify not to cache bundles.
1280                        if (isBaseBundle && baseBundle == null) {
1281                            baseBundle = bundle;
1282                        }
1283                    }
1284                }
1285
1286                if (bundle == null) {
1287                    if (baseBundle == null) {
1288                        throwMissingResourceException(baseName, locale,
1289                                cacheKey.getCause());
1290                    }
1291                    bundle = baseBundle;
1292                }
1293
1294                return bundle;
1295            }
1296
1297            /**
1298             * Checks if the given <code>List</code> is not null, not empty,
1299             * not having null in its elements.
1300             */
1301            private static final boolean checkList(List a) {
1302                boolean valid = (a != null && a.size() != 0);
1303                if (valid) {
1304                    int size = a.size();
1305                    for (int i = 0; valid && i < size; i++) {
1306                        valid = (a.get(i) != null);
1307                    }
1308                }
1309                return valid;
1310            }
1311
1312            private static final ResourceBundle findBundle(CacheKey cacheKey,
1313                    List<Locale> candidateLocales, List<String> formats,
1314                    int index, Control control, ResourceBundle baseBundle) {
1315                Locale targetLocale = candidateLocales.get(index);
1316                ResourceBundle parent = null;
1317                if (index != candidateLocales.size() - 1) {
1318                    parent = findBundle(cacheKey, candidateLocales, formats,
1319                            index + 1, control, baseBundle);
1320                } else if (baseBundle != null
1321                        && Locale.ROOT.equals(targetLocale)) {
1322                    return baseBundle;
1323                }
1324
1325                // Before we do the real loading work, see whether we need to
1326                // do some housekeeping: If references to class loaders or
1327                // resource bundles have been nulled out, remove all related
1328                // information from the cache.
1329                Object ref;
1330                while ((ref = referenceQueue.poll()) != null) {
1331                    cacheList.remove(((CacheKeyReference) ref).getCacheKey());
1332                }
1333
1334                // flag indicating the resource bundle has expired in the cache
1335                boolean expiredBundle = false;
1336
1337                // First, look up the cache to see if it's in the cache, without
1338                // declaring beginLoading.
1339                cacheKey.setLocale(targetLocale);
1340                ResourceBundle bundle = findBundleInCache(cacheKey, control);
1341                if (isValidBundle(bundle)) {
1342                    expiredBundle = bundle.expired;
1343                    if (!expiredBundle) {
1344                        // If its parent is the one asked for by the candidate
1345                        // locales (the runtime lookup path), we can take the cached
1346                        // one. (If it's not identical, then we'd have to check the
1347                        // parent's parents to be consistent with what's been
1348                        // requested.)
1349                        if (bundle.parent == parent) {
1350                            return bundle;
1351                        }
1352                        // Otherwise, remove the cached one since we can't keep
1353                        // the same bundles having different parents.
1354                        BundleReference bundleRef = cacheList.get(cacheKey);
1355                        if (bundleRef != null && bundleRef.get() == bundle) {
1356                            cacheList.remove(cacheKey, bundleRef);
1357                        }
1358                    }
1359                }
1360
1361                if (bundle != NONEXISTENT_BUNDLE) {
1362                    CacheKey constKey = (CacheKey) cacheKey.clone();
1363
1364                    try {
1365                        // Try declaring loading. If beginLoading() returns true,
1366                        // then we can proceed. Otherwise, we need to take a look
1367                        // at the cache again to see if someone else has loaded
1368                        // the bundle and put it in the cache while we've been
1369                        // waiting for other loading work to complete.
1370                        while (!beginLoading(constKey)) {
1371                            bundle = findBundleInCache(cacheKey, control);
1372                            if (bundle == null) {
1373                                continue;
1374                            }
1375                            if (bundle == NONEXISTENT_BUNDLE) {
1376                                // If the bundle is NONEXISTENT_BUNDLE, the bundle doesn't exist.
1377                                return parent;
1378                            }
1379                            expiredBundle = bundle.expired;
1380                            if (!expiredBundle) {
1381                                if (bundle.parent == parent) {
1382                                    return bundle;
1383                                }
1384                                BundleReference bundleRef = cacheList
1385                                        .get(cacheKey);
1386                                if (bundleRef != null
1387                                        && bundleRef.get() == bundle) {
1388                                    cacheList.remove(cacheKey, bundleRef);
1389                                }
1390                            }
1391                        }
1392
1393                        try {
1394                            bundle = loadBundle(cacheKey, formats, control,
1395                                    expiredBundle);
1396                            if (bundle != null) {
1397                                if (bundle.parent == null) {
1398                                    bundle.setParent(parent);
1399                                }
1400                                bundle.locale = targetLocale;
1401                                bundle = putBundleInCache(cacheKey, bundle,
1402                                        control);
1403                                return bundle;
1404                            }
1405
1406                            // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle
1407                            // instance for the locale.
1408                            putBundleInCache(cacheKey, NONEXISTENT_BUNDLE,
1409                                    control);
1410                        } finally {
1411                            endLoading(constKey);
1412                        }
1413                    } finally {
1414                        if (constKey.getCause() instanceof  InterruptedException) {
1415                            Thread.currentThread().interrupt();
1416                        }
1417                    }
1418                }
1419                assert underConstruction.get(cacheKey) != Thread
1420                        .currentThread();
1421                return parent;
1422            }
1423
1424            private static final ResourceBundle loadBundle(CacheKey cacheKey,
1425                    List<String> formats, Control control, boolean reload) {
1426                assert underConstruction.get(cacheKey) == Thread
1427                        .currentThread();
1428
1429                // Here we actually load the bundle in the order of formats
1430                // specified by the getFormats() value.
1431                Locale targetLocale = cacheKey.getLocale();
1432
1433                ResourceBundle bundle = null;
1434                int size = formats.size();
1435                for (int i = 0; i < size; i++) {
1436                    String format = formats.get(i);
1437                    try {
1438                        bundle = control.newBundle(cacheKey.getName(),
1439                                targetLocale, format, cacheKey.getLoader(),
1440                                reload);
1441                    } catch (LinkageError error) {
1442                        // We need to handle the LinkageError case due to
1443                        // inconsistent case-sensitivity in ClassLoader.
1444                        // See 6572242 for details.
1445                        cacheKey.setCause(error);
1446                    } catch (Exception cause) {
1447                        cacheKey.setCause(cause);
1448                    }
1449                    if (bundle != null) {
1450                        // Set the format in the cache key so that it can be
1451                        // used when calling needsReload later.
1452                        cacheKey.setFormat(format);
1453                        bundle.name = cacheKey.getName();
1454                        bundle.locale = targetLocale;
1455                        // Bundle provider might reuse instances. So we should make
1456                        // sure to clear the expired flag here.
1457                        bundle.expired = false;
1458                        break;
1459                    }
1460                }
1461                assert underConstruction.get(cacheKey) == Thread
1462                        .currentThread();
1463
1464                return bundle;
1465            }
1466
1467            private static final boolean isValidBundle(ResourceBundle bundle) {
1468                return bundle != null && bundle != NONEXISTENT_BUNDLE;
1469            }
1470
1471            /**
1472             * Determines whether any of resource bundles in the parent chain,
1473             * including the leaf, have expired.
1474             */
1475            private static final boolean hasValidParentChain(
1476                    ResourceBundle bundle) {
1477                long now = System.currentTimeMillis();
1478                while (bundle != null) {
1479                    if (bundle.expired) {
1480                        return false;
1481                    }
1482                    CacheKey key = bundle.cacheKey;
1483                    if (key != null) {
1484                        long expirationTime = key.expirationTime;
1485                        if (expirationTime >= 0 && expirationTime <= now) {
1486                            return false;
1487                        }
1488                    }
1489                    bundle = bundle.parent;
1490                }
1491                return true;
1492            }
1493
1494            /**
1495             * Declares the beginning of actual resource bundle loading. This method
1496             * returns true if the declaration is successful and the current thread has
1497             * been put in underConstruction. If someone else has already begun
1498             * loading, this method waits until that loading work is complete and
1499             * returns false.
1500             */
1501            private static final boolean beginLoading(CacheKey constKey) {
1502                Thread me = Thread.currentThread();
1503                Thread worker;
1504                // We need to declare by putting the current Thread (me) to
1505                // underConstruction that we are working on loading the specified
1506                // resource bundle. If we are already working the loading, it means
1507                // that the resource loading requires a recursive call. In that case,
1508                // we have to proceed. (4300693)
1509                if (((worker = underConstruction.putIfAbsent(constKey, me)) == null)
1510                        || worker == me) {
1511                    return true;
1512                }
1513
1514                // If someone else is working on the loading, wait until
1515                // the Thread finishes the bundle loading.
1516                synchronized (worker) {
1517                    while (underConstruction.get(constKey) == worker) {
1518                        try {
1519                            worker.wait();
1520                        } catch (InterruptedException e) {
1521                            // record the interruption
1522                            constKey.setCause(e);
1523                        }
1524                    }
1525                }
1526                return false;
1527            }
1528
1529            /**
1530             * Declares the end of the bundle loading. This method calls notifyAll
1531             * for those who are waiting for this completion.
1532             */
1533            private static final void endLoading(CacheKey constKey) {
1534                // Remove this Thread from the underConstruction map and wake up
1535                // those who have been waiting for me to complete this bundle
1536                // loading.
1537                Thread me = Thread.currentThread();
1538                assert (underConstruction.get(constKey) == me);
1539                underConstruction.remove(constKey);
1540                synchronized (me) {
1541                    me.notifyAll();
1542                }
1543            }
1544
1545            /**
1546             * Throw a MissingResourceException with proper message
1547             */
1548            private static final void throwMissingResourceException(
1549                    String baseName, Locale locale, Throwable cause) {
1550                // If the cause is a MissingResourceException, avoid creating
1551                // a long chain. (6355009)
1552                if (cause instanceof  MissingResourceException) {
1553                    cause = null;
1554                }
1555                throw new MissingResourceException(
1556                        "Can't find bundle for base name " + baseName
1557                                + ", locale " + locale,
1558                        baseName + "_" + locale, // className
1559                        "", // key
1560                        cause);
1561            }
1562
1563            /**
1564             * Finds a bundle in the cache. Any expired bundles are marked as
1565             * `expired' and removed from the cache upon return.
1566             *
1567             * @param cacheKey the key to look up the cache
1568             * @param control the Control to be used for the expiration control
1569             * @return the cached bundle, or null if the bundle is not found in the
1570             * cache or its parent has expired. <code>bundle.expire</code> is true
1571             * upon return if the bundle in the cache has expired.
1572             */
1573            private static final ResourceBundle findBundleInCache(
1574                    CacheKey cacheKey, Control control) {
1575                BundleReference bundleRef = cacheList.get(cacheKey);
1576                if (bundleRef == null) {
1577                    return null;
1578                }
1579                ResourceBundle bundle = bundleRef.get();
1580                if (bundle == null) {
1581                    return null;
1582                }
1583                ResourceBundle p = bundle.parent;
1584                assert p != NONEXISTENT_BUNDLE;
1585                // If the parent has expired, then this one must also expire. We
1586                // check only the immediate parent because the actual loading is
1587                // done from the root (base) to leaf (child) and the purpose of
1588                // checking is to propagate expiration towards the leaf. For
1589                // example, if the requested locale is ja_JP_JP and there are
1590                // bundles for all of the candidates in the cache, we have a list,
1591                //
1592                // base <- ja <- ja_JP <- ja_JP_JP
1593                //
1594                // If ja has expired, then it will reload ja and the list becomes a
1595                // tree.
1596                //
1597                // base <- ja (new)
1598                //  "   <- ja (expired) <- ja_JP <- ja_JP_JP
1599                //
1600                // When looking up ja_JP in the cache, it finds ja_JP in the cache
1601                // which references to the expired ja. Then, ja_JP is marked as
1602                // expired and removed from the cache. This will be propagated to
1603                // ja_JP_JP.
1604                //
1605                // Now, it's possible, for example, that while loading new ja_JP,
1606                // someone else has started loading the same bundle and finds the
1607                // base bundle has expired. Then, what we get from the first
1608                // getBundle call includes the expired base bundle. However, if
1609                // someone else didn't start its loading, we wouldn't know if the
1610                // base bundle has expired at the end of the loading process. The
1611                // expiration control doesn't guarantee that the returned bundle and
1612                // its parents haven't expired.
1613                //
1614                // We could check the entire parent chain to see if there's any in
1615                // the chain that has expired. But this process may never end. An
1616                // extreme case would be that getTimeToLive returns 0 and
1617                // needsReload always returns true.
1618                if (p != null && p.expired) {
1619                    assert bundle != NONEXISTENT_BUNDLE;
1620                    bundle.expired = true;
1621                    bundle.cacheKey = null;
1622                    cacheList.remove(cacheKey, bundleRef);
1623                    bundle = null;
1624                } else {
1625                    CacheKey key = bundleRef.getCacheKey();
1626                    long expirationTime = key.expirationTime;
1627                    if (!bundle.expired && expirationTime >= 0
1628                            && expirationTime <= System.currentTimeMillis()) {
1629                        // its TTL period has expired.
1630                        if (bundle != NONEXISTENT_BUNDLE) {
1631                            // Synchronize here to call needsReload to avoid
1632                            // redundant concurrent calls for the same bundle.
1633                            synchronized (bundle) {
1634                                expirationTime = key.expirationTime;
1635                                if (!bundle.expired
1636                                        && expirationTime >= 0
1637                                        && expirationTime <= System
1638                                                .currentTimeMillis()) {
1639                                    try {
1640                                        bundle.expired = control.needsReload(
1641                                                key.getName(), key.getLocale(),
1642                                                key.getFormat(), key
1643                                                        .getLoader(), bundle,
1644                                                key.loadTime);
1645                                    } catch (Exception e) {
1646                                        cacheKey.setCause(e);
1647                                    }
1648                                    if (bundle.expired) {
1649                                        // If the bundle needs to be reloaded, then
1650                                        // remove the bundle from the cache, but
1651                                        // return the bundle with the expired flag
1652                                        // on.
1653                                        bundle.cacheKey = null;
1654                                        cacheList.remove(cacheKey, bundleRef);
1655                                    } else {
1656                                        // Update the expiration control info. and reuse
1657                                        // the same bundle instance
1658                                        setExpirationTime(key, control);
1659                                    }
1660                                }
1661                            }
1662                        } else {
1663                            // We just remove NONEXISTENT_BUNDLE from the cache.
1664                            cacheList.remove(cacheKey, bundleRef);
1665                            bundle = null;
1666                        }
1667                    }
1668                }
1669                return bundle;
1670            }
1671
1672            /**
1673             * Put a new bundle in the cache.
1674             *
1675             * @param cacheKey the key for the resource bundle
1676             * @param bundle the resource bundle to be put in the cache
1677             * @return the ResourceBundle for the cacheKey; if someone has put
1678             * the bundle before this call, the one found in the cache is
1679             * returned.
1680             */
1681            private static final ResourceBundle putBundleInCache(
1682                    CacheKey cacheKey, ResourceBundle bundle, Control control) {
1683                setExpirationTime(cacheKey, control);
1684                if (cacheKey.expirationTime != Control.TTL_DONT_CACHE) {
1685                    CacheKey key = (CacheKey) cacheKey.clone();
1686                    BundleReference bundleRef = new BundleReference(bundle,
1687                            referenceQueue, key);
1688                    bundle.cacheKey = key;
1689
1690                    // Put the bundle in the cache if it's not been in the cache.
1691                    BundleReference result = cacheList.putIfAbsent(key,
1692                            bundleRef);
1693
1694                    // If someone else has put the same bundle in the cache before
1695                    // us and it has not expired, we should use the one in the cache.
1696                    if (result != null) {
1697                        ResourceBundle rb = result.get();
1698                        if (rb != null && !rb.expired) {
1699                            // Clear the back link to the cache key
1700                            bundle.cacheKey = null;
1701                            bundle = rb;
1702                            // Clear the reference in the BundleReference so that
1703                            // it won't be enqueued.
1704                            bundleRef.clear();
1705                        } else {
1706                            // Replace the invalid (garbage collected or expired)
1707                            // instance with the valid one.
1708                            cacheList.put(key, bundleRef);
1709                        }
1710                    }
1711                }
1712                return bundle;
1713            }
1714
1715            private static final void setExpirationTime(CacheKey cacheKey,
1716                    Control control) {
1717                long ttl = control.getTimeToLive(cacheKey.getName(), cacheKey
1718                        .getLocale());
1719                if (ttl >= 0) {
1720                    // If any expiration time is specified, set the time to be
1721                    // expired in the cache.
1722                    long now = System.currentTimeMillis();
1723                    cacheKey.loadTime = now;
1724                    cacheKey.expirationTime = now + ttl;
1725                } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
1726                    cacheKey.expirationTime = ttl;
1727                } else {
1728                    throw new IllegalArgumentException("Invalid Control: TTL="
1729                            + ttl);
1730                }
1731            }
1732
1733            /**
1734             * Removes all resource bundles from the cache that have been loaded
1735             * using the caller's class loader.
1736             *
1737             * @since 1.6
1738             * @see ResourceBundle.Control#getTimeToLive(String,Locale)
1739             */
1740            public static final void clearCache() {
1741                clearCache(getLoader());
1742            }
1743
1744            /**
1745             * Removes all resource bundles from the cache that have been loaded
1746             * using the given class loader.
1747             *
1748             * @param loader the class loader
1749             * @exception NullPointerException if <code>loader</code> is null
1750             * @since 1.6
1751             * @see ResourceBundle.Control#getTimeToLive(String,Locale)
1752             */
1753            public static final void clearCache(ClassLoader loader) {
1754                if (loader == null) {
1755                    throw new NullPointerException();
1756                }
1757                Set<CacheKey> set = cacheList.keySet();
1758                for (CacheKey key : set) {
1759                    if (key.getLoader() == loader) {
1760                        set.remove(key);
1761                    }
1762                }
1763            }
1764
1765            /**
1766             * Gets an object for the given key from this resource bundle.
1767             * Returns null if this resource bundle does not contain an
1768             * object for the given key.
1769             *
1770             * @param key the key for the desired object
1771             * @exception NullPointerException if <code>key</code> is <code>null</code>
1772             * @return the object for the given key, or null
1773             */
1774            protected abstract Object handleGetObject(String key);
1775
1776            /**
1777             * Returns an enumeration of the keys.
1778             *
1779             * @return an <code>Enumeration</code> of the keys contained in
1780             *         this <code>ResourceBundle</code> and its parent bundles.
1781             */
1782            public abstract Enumeration<String> getKeys();
1783
1784            /**
1785             * Determines whether the given <code>key</code> is contained in
1786             * this <code>ResourceBundle</code> or its parent bundles.
1787             *
1788             * @param key
1789             *        the resource <code>key</code>
1790             * @return <code>true</code> if the given <code>key</code> is
1791             *        contained in this <code>ResourceBundle</code> or its
1792             *        parent bundles; <code>false</code> otherwise.
1793             * @exception NullPointerException
1794             *         if <code>key</code> is <code>null</code>
1795             * @since 1.6
1796             */
1797            public boolean containsKey(String key) {
1798                if (key == null) {
1799                    throw new NullPointerException();
1800                }
1801                for (ResourceBundle rb = this ; rb != null; rb = rb.parent) {
1802                    if (rb.handleKeySet().contains(key)) {
1803                        return true;
1804                    }
1805                }
1806                return false;
1807            }
1808
1809            /**
1810             * Returns a <code>Set</code> of all keys contained in this
1811             * <code>ResourceBundle</code> and its parent bundles.
1812             *
1813             * @return a <code>Set</code> of all keys contained in this
1814             *         <code>ResourceBundle</code> and its parent bundles.
1815             * @since 1.6
1816             */
1817            public Set<String> keySet() {
1818                Set<String> keys = new HashSet<String>();
1819                for (ResourceBundle rb = this ; rb != null; rb = rb.parent) {
1820                    keys.addAll(rb.handleKeySet());
1821                }
1822                return keys;
1823            }
1824
1825            /**
1826             * Returns a <code>Set</code> of the keys contained <em>only</em>
1827             * in this <code>ResourceBundle</code>.
1828             *
1829             * <p>The default implementation returns a <code>Set</code> of the
1830             * keys returned by the {@link #getKeys() getKeys} method except
1831             * for the ones for which the {@link #handleGetObject(String)
1832             * handleGetObject} method returns <code>null</code>. Once the
1833             * <code>Set</code> has been created, the value is kept in this
1834             * <code>ResourceBundle</code> in order to avoid producing the
1835             * same <code>Set</code> in the next calls.  Override this method
1836             * in subclass implementations for faster handling.
1837             *
1838             * @return a <code>Set</code> of the keys contained only in this
1839             *        <code>ResourceBundle</code>
1840             * @since 1.6
1841             */
1842            protected Set<String> handleKeySet() {
1843                if (keySet == null) {
1844                    synchronized (this ) {
1845                        if (keySet == null) {
1846                            Set<String> keys = new HashSet<String>();
1847                            Enumeration<String> enumKeys = getKeys();
1848                            while (enumKeys.hasMoreElements()) {
1849                                String key = enumKeys.nextElement();
1850                                if (handleGetObject(key) != null) {
1851                                    keys.add(key);
1852                                }
1853                            }
1854                            keySet = keys;
1855                        }
1856                    }
1857                }
1858                return keySet;
1859            }
1860
1861            /**
1862             * <code>ResourceBundle.Control</code> defines a set of callback methods
1863             * that are invoked by the {@link ResourceBundle#getBundle(String,
1864             * Locale, ClassLoader, Control) ResourceBundle.getBundle} factory
1865             * methods during the bundle loading process. In other words, a
1866             * <code>ResourceBundle.Control</code> collaborates with the factory
1867             * methods for loading resource bundles. The default implementation of
1868             * the callback methods provides the information necessary for the
1869             * factory methods to perform the <a
1870             * href="./ResourceBundle.html#default_behavior">default behavior</a>.
1871             *
1872             * <p>In addition to the callback methods, the {@link
1873             * #toBundleName(String, Locale) toBundleName} and {@link
1874             * #toResourceName(String, String) toResourceName} methods are defined
1875             * primarily for convenience in implementing the callback
1876             * methods. However, the <code>toBundleName</code> method could be
1877             * overridden to provide different conventions in the organization and
1878             * packaging of localized resources.  The <code>toResourceName</code>
1879             * method is <code>final</code> to avoid use of wrong resource and class
1880             * name separators.
1881             *
1882             * <p>Two factory methods, {@link #getControl(List)} and {@link
1883             * #getNoFallbackControl(List)}, provide
1884             * <code>ResourceBundle.Control</code> instances that implement common
1885             * variations of the default bundle loading process.
1886             *
1887             * <p>The formats returned by the {@link Control#getFormats(String)
1888             * getFormats} method and candidate locales returned by the {@link
1889             * ResourceBundle.Control#getCandidateLocales(String, Locale)
1890             * getCandidateLocales} method must be consistent in all
1891             * <code>ResourceBundle.getBundle</code> invocations for the same base
1892             * bundle. Otherwise, the <code>ResourceBundle.getBundle</code> methods
1893             * may return unintended bundles. For example, if only
1894             * <code>"java.class"</code> is returned by the <code>getFormats</code>
1895             * method for the first call to <code>ResourceBundle.getBundle</code>
1896             * and only <code>"java.properties"</code> for the second call, then the
1897             * second call will return the class-based one that has been cached
1898             * during the first call.
1899             *
1900             * <p>A <code>ResourceBundle.Control</code> instance must be thread-safe
1901             * if it's simultaneously used by multiple threads.
1902             * <code>ResourceBundle.getBundle</code> does not synchronize to call
1903             * the <code>ResourceBundle.Control</code> methods. The default
1904             * implementations of the methods are thread-safe.
1905             *
1906             * <p>Applications can specify <code>ResourceBundle.Control</code>
1907             * instances returned by the <code>getControl</code> factory methods or
1908             * created from a subclass of <code>ResourceBundle.Control</code> to
1909             * customize the bundle loading process. The following are examples of
1910             * changing the default bundle loading process.
1911             *
1912             * <p><b>Example 1</b>
1913             *
1914             * <p>The following code lets <code>ResourceBundle.getBundle</code> look
1915             * up only properties-based resources.
1916             *
1917             * <pre>
1918             * import java.util.*;
1919             * import static java.util.ResourceBundle.Control.*;
1920             * ...
1921             * ResourceBundle bundle =
1922             *   ResourceBundle.getBundle("MyResources", new Locale("fr", "CH"),
1923             *                            ResourceBundle.Control.getControl(FORMAT_PROPERTIES));
1924             * </pre>
1925             *
1926             * Given the resource bundles in the <a
1927             * href="./ResourceBundle.html#default_behavior_example">example</a> in
1928             * the <code>ResourceBundle.getBundle</code> description, this
1929             * <code>ResourceBundle.getBundle</code> call loads
1930             * <code>MyResources_fr_CH.properties</code> whose parent is
1931             * <code>MyResources_fr.properties</code> whose parent is
1932             * <code>MyResources.properties</code>. (<code>MyResources_fr_CH.properties</code>
1933             * is not hidden, but <code>MyResources_fr_CH.class</code> is.)
1934             *
1935             * <p><b>Example 2</b>
1936             *
1937             * <p>The following is an example of loading XML-based bundles
1938             * using {@link Properties#loadFromXML(java.io.InputStream)
1939             * Properties.loadFromXML}.
1940             *
1941             * <pre>
1942             * ResourceBundle rb = ResourceBundle.getBundle("Messages",
1943             *     new ResourceBundle.Control() {
1944             *         public List&lt;String&gt; getFormats(String baseName) {
1945             *             if (baseName == null)
1946             *                 throw new NullPointerException();
1947             *             return Arrays.asList("xml");
1948             *         }
1949             *         public ResourceBundle newBundle(String baseName,
1950             *                                         Locale locale,
1951             *                                         String format,
1952             *                                         ClassLoader loader,
1953             *                                         boolean reload)
1954             *                          throws IllegalAccessException,
1955             *                                 InstantiationException,
1956             *                                 IOException {
1957             *             if (baseName == null || locale == null
1958             *                   || format == null || loader == null)
1959             *                 throw new NullPointerException();
1960             *             ResourceBundle bundle = null;
1961             *             if (format.equals("xml")) {
1962             *                 String bundleName = toBundleName(baseName, locale);
1963             *                 String resourceName = toResourceName(bundleName, format);
1964             *                 InputStream stream = null;
1965             *                 if (reload) {
1966             *                     URL url = loader.getResource(resourceName);
1967             *                     if (url != null) {
1968             *                         URLConnection connection = url.openConnection();
1969             *                         if (connection != null) {
1970             *                             // Disable caches to get fresh data for
1971             *                             // reloading.
1972             *                             connection.setUseCaches(false);
1973             *                             stream = connection.getInputStream();
1974             *                         }
1975             *                     }
1976             *                 } else {
1977             *                     stream = loader.getResourceAsStream(resourceName);
1978             *                 }
1979             *                 if (stream != null) {
1980             *                     BufferedInputStream bis = new BufferedInputStream(stream);
1981             *                     bundle = new XMLResourceBundle(bis);
1982             *                     bis.close();
1983             *                 }
1984             *             }
1985             *             return bundle;
1986             *         }
1987             *     });
1988             *
1989             * ...
1990             *
1991             * private static class XMLResourceBundle extends ResourceBundle {
1992             *     private Properties props;
1993             *     XMLResourceBundle(InputStream stream) throws IOException {
1994             *         props = new Properties();
1995             *         props.loadFromXML(stream);
1996             *     }
1997             *     protected Object handleGetObject(String key) {
1998             *         return props.getProperty(key);
1999             *     }
2000             *     public Enumeration&lt;String&gt; getKeys() {
2001             *         ...
2002             *     }
2003             * }
2004             * </pre>
2005             * 
2006             * @since 1.6
2007             */
2008            public static class Control {
2009                /**
2010                 * The default format <code>List</code>, which contains the strings
2011                 * <code>"java.class"</code> and <code>"java.properties"</code>, in
2012                 * this order. This <code>List</code> is {@linkplain
2013                 * Collections#unmodifiableList(List) unmodifiable}.
2014                 *
2015                 * @see #getFormats(String)
2016                 */
2017                public static final List<String> FORMAT_DEFAULT = Collections
2018                        .unmodifiableList(Arrays.asList("java.class",
2019                                "java.properties"));
2020
2021                /**
2022                 * The class-only format <code>List</code> containing
2023                 * <code>"java.class"</code>. This <code>List</code> is {@linkplain
2024                 * Collections#unmodifiableList(List) unmodifiable}.
2025                 *
2026                 * @see #getFormats(String)
2027                 */
2028                public static final List<String> FORMAT_CLASS = Collections
2029                        .unmodifiableList(Arrays.asList("java.class"));
2030
2031                /**
2032                 * The properties-only format <code>List</code> containing
2033                 * <code>"java.properties"</code>. This <code>List</code> is
2034                 * {@linkplain Collections#unmodifiableList(List) unmodifiable}.
2035                 *
2036                 * @see #getFormats(String)
2037                 */
2038                public static final List<String> FORMAT_PROPERTIES = Collections
2039                        .unmodifiableList(Arrays.asList("java.properties"));
2040
2041                /**
2042                 * The time-to-live constant for not caching loaded resource bundle
2043                 * instances.
2044                 *
2045                 * @see #getTimeToLive(String, Locale)
2046                 */
2047                public static final long TTL_DONT_CACHE = -1;
2048
2049                /**
2050                 * The time-to-live constant for disabling the expiration control
2051                 * for loaded resource bundle instances in the cache.
2052                 *
2053                 * @see #getTimeToLive(String, Locale)
2054                 */
2055                public static final long TTL_NO_EXPIRATION_CONTROL = -2;
2056
2057                private static final Control INSTANCE = new Control();
2058
2059                /**
2060                 * Sole constructor. (For invocation by subclass constructors,
2061                 * typically implicit.)
2062                 */
2063                protected Control() {
2064                }
2065
2066                /**
2067                 * Returns a <code>ResourceBundle.Control</code> in which the {@link
2068                 * #getFormats(String) getFormats} method returns the specified
2069                 * <code>formats</code>. The <code>formats</code> must be equal to
2070                 * one of {@link Control#FORMAT_PROPERTIES}, {@link
2071                 * Control#FORMAT_CLASS} or {@link
2072                 * Control#FORMAT_DEFAULT}. <code>ResourceBundle.Control</code>
2073                 * instances returned by this method are singletons and thread-safe.
2074                 *
2075                 * <p>Specifying {@link Control#FORMAT_DEFAULT} is equivalent to
2076                 * instantiating the <code>ResourceBundle.Control</code> class,
2077                 * except that this method returns a singleton.
2078                 *
2079                 * @param formats
2080                 *        the formats to be returned by the
2081                 *        <code>ResourceBundle.Control.getFormats</code> method
2082                 * @return a <code>ResourceBundle.Control</code> supporting the
2083                 *        specified <code>formats</code>
2084                 * @exception NullPointerException
2085                 *        if <code>formats</code> is <code>null</code>
2086                 * @exception IllegalArgumentException
2087                 *        if <code>formats</code> is unknown
2088                 */
2089                public static final Control getControl(List<String> formats) {
2090                    if (formats.equals(Control.FORMAT_PROPERTIES)) {
2091                        return SingleFormatControl.PROPERTIES_ONLY;
2092                    }
2093                    if (formats.equals(Control.FORMAT_CLASS)) {
2094                        return SingleFormatControl.CLASS_ONLY;
2095                    }
2096                    if (formats.equals(Control.FORMAT_DEFAULT)) {
2097                        return Control.INSTANCE;
2098                    }
2099                    throw new IllegalArgumentException();
2100                }
2101
2102                /**
2103                 * Returns a <code>ResourceBundle.Control</code> in which the {@link
2104                 * #getFormats(String) getFormats} method returns the specified
2105                 * <code>formats</code> and the {@link
2106                 * Control#getFallbackLocale(String, Locale) getFallbackLocale}
2107                 * method returns <code>null</code>. The <code>formats</code> must
2108                 * be equal to one of {@link Control#FORMAT_PROPERTIES}, {@link
2109                 * Control#FORMAT_CLASS} or {@link Control#FORMAT_DEFAULT}.
2110                 * <code>ResourceBundle.Control</code> instances returned by this
2111                 * method are singletons and thread-safe.
2112                 *
2113                 * @param formats
2114                 *        the formats to be returned by the
2115                 *        <code>ResourceBundle.Control.getFormats</code> method
2116                 * @return a <code>ResourceBundle.Control</code> supporting the
2117                 *        specified <code>formats</code> with no fallback
2118                 *        <code>Locale</code> support
2119                 * @exception NullPointerException
2120                 *        if <code>formats</code> is <code>null</code>
2121                 * @exception IllegalArgumentException
2122                 *        if <code>formats</code> is unknown
2123                 */
2124                public static final Control getNoFallbackControl(
2125                        List<String> formats) {
2126                    if (formats.equals(Control.FORMAT_DEFAULT)) {
2127                        return NoFallbackControl.NO_FALLBACK;
2128                    }
2129                    if (formats.equals(Control.FORMAT_PROPERTIES)) {
2130                        return NoFallbackControl.PROPERTIES_ONLY_NO_FALLBACK;
2131                    }
2132                    if (formats.equals(Control.FORMAT_CLASS)) {
2133                        return NoFallbackControl.CLASS_ONLY_NO_FALLBACK;
2134                    }
2135                    throw new IllegalArgumentException();
2136                }
2137
2138                /**
2139                 * Returns a <code>List</code> of <code>String</code>s containing
2140                 * formats to be used to load resource bundles for the given
2141                 * <code>baseName</code>. The <code>ResourceBundle.getBundle</code>
2142                 * factory method tries to load resource bundles with formats in the
2143                 * order specified by the list. The list returned by this method
2144                 * must have at least one <code>String</code>. The predefined
2145                 * formats are <code>"java.class"</code> for class-based resource
2146                 * bundles and <code>"java.properties"</code> for {@linkplain
2147                 * PropertyResourceBundle properties-based} ones. Strings starting
2148                 * with <code>"java."</code> are reserved for future extensions and
2149                 * must not be used by application-defined formats.
2150                 *
2151                 * <p>It is not a requirement to return an immutable (unmodifiable)
2152                 * <code>List</code>.  However, the returned <code>List</code> must
2153                 * not be mutated after it has been returned by
2154                 * <code>getFormats</code>.
2155                 *
2156                 * <p>The default implementation returns {@link #FORMAT_DEFAULT} so
2157                 * that the <code>ResourceBundle.getBundle</code> factory method
2158                 * looks up first class-based resource bundles, then
2159                 * properties-based ones.
2160                 *
2161                 * @param baseName
2162                 *        the base name of the resource bundle, a fully qualified class
2163                 *        name
2164                 * @return a <code>List</code> of <code>String</code>s containing
2165                 *        formats for loading resource bundles.
2166                 * @exception NullPointerException
2167                 *        if <code>baseName</code> is null
2168                 * @see #FORMAT_DEFAULT
2169                 * @see #FORMAT_CLASS
2170                 * @see #FORMAT_PROPERTIES
2171                 */
2172                public List<String> getFormats(String baseName) {
2173                    if (baseName == null) {
2174                        throw new NullPointerException();
2175                    }
2176                    return FORMAT_DEFAULT;
2177                }
2178
2179                /**
2180                 * Returns a <code>List</code> of <code>Locale</code>s as candidate
2181                 * locales for <code>baseName</code> and <code>locale</code>. This
2182                 * method is called by the <code>ResourceBundle.getBundle</code>
2183                 * factory method each time the factory method tries finding a
2184                 * resource bundle for a target <code>Locale</code>.
2185                 *
2186                 * <p>The sequence of the candidate locales also corresponds to the
2187                 * runtime resource lookup path (also known as the <I>parent
2188                 * chain</I>), if the corresponding resource bundles for the
2189                 * candidate locales exist and their parents are not defined by
2190                 * loaded resource bundles themselves.  The last element of the list
2191                 * must be a {@linkplain Locale#ROOT root locale} if it is desired to 
2192                 * have the base bundle as the terminal of the parent chain.
2193                 *
2194                 * <p>If the given locale is equal to <code>Locale.ROOT</code> (the
2195                 * root locale), a <code>List</code> containing only the root
2196                 * <code>Locale</code> must be returned. In this case, the
2197                 * <code>ResourceBundle.getBundle</code> factory method loads only
2198                 * the base bundle as the resulting resource bundle.
2199                 *
2200                 * <p>It is not a requirement to return an immutable
2201                 * (unmodifiable) <code>List</code>. However, the returned
2202                 * <code>List</code> must not be mutated after it has been
2203                 * returned by <code>getCandidateLocales</code>.
2204                 *
2205                 * <p>The default implementation returns a <code>List</code> containing
2206                 * <code>Locale</code>s in the following sequence:
2207                 * <pre>
2208                 *     Locale(language, country, variant)
2209                 *     Locale(language, country)
2210                 *     Locale(language)
2211                 *     Locale.ROOT
2212                 * </pre>
2213                 * where <code>language</code>, <code>country</code> and
2214                 * <code>variant</code> are the language, country and variant values
2215                 * of the given <code>locale</code>, respectively. Locales where the
2216                 * final component values are empty strings are omitted.
2217                 *
2218                 * <p>The default implementation uses an {@link ArrayList} that
2219                 * overriding implementations may modify before returning it to the
2220                 * caller. However, a subclass must not modify it after it has
2221                 * been returned by <code>getCandidateLocales</code>.
2222                 *
2223                 * <p>For example, if the given <code>baseName</code> is "Messages"
2224                 * and the given <code>locale</code> is
2225                 * <code>Locale("ja",&nbsp;"",&nbsp;"XX")</code>, then a
2226                 * <code>List</code> of <code>Locale</code>s:
2227                 * <pre>
2228                 *     Locale("ja", "", "XX")
2229                 *     Locale("ja")
2230                 *     Locale.ROOT
2231                 * </pre>
2232                 * is returned. And if the resource bundles for the "ja" and
2233                 * "" <code>Locale</code>s are found, then the runtime resource
2234                 * lookup path (parent chain) is:
2235                 * <pre>
2236                 *     Messages_ja -> Messages
2237                 * </pre>
2238                 *
2239                 * @param baseName
2240                 *        the base name of the resource bundle, a fully
2241                 *        qualified class name
2242                 * @param locale
2243                 *        the locale for which a resource bundle is desired
2244                 * @return a <code>List</code> of candidate
2245                 *        <code>Locale</code>s for the given <code>locale</code>
2246                 * @exception NullPointerException
2247                 *        if <code>baseName</code> or <code>locale</code> is
2248                 *        <code>null</code>
2249                 */
2250                public List<Locale> getCandidateLocales(String baseName,
2251                        Locale locale) {
2252                    if (baseName == null) {
2253                        throw new NullPointerException();
2254                    }
2255                    String language = locale.getLanguage();
2256                    String country = locale.getCountry();
2257                    String variant = locale.getVariant();
2258
2259                    List<Locale> locales = new ArrayList<Locale>(4);
2260                    if (variant.length() > 0) {
2261                        locales.add(locale);
2262                    }
2263                    if (country.length() > 0) {
2264                        locales.add((locales.size() == 0) ? locale : Locale
2265                                .getInstance(language, country, ""));
2266                    }
2267                    if (language.length() > 0) {
2268                        locales.add((locales.size() == 0) ? locale : Locale
2269                                .getInstance(language, "", ""));
2270                    }
2271                    locales.add(Locale.ROOT);
2272                    return locales;
2273                }
2274
2275                /**
2276                 * Returns a <code>Locale</code> to be used as a fallback locale for
2277                 * further resource bundle searches by the
2278                 * <code>ResourceBundle.getBundle</code> factory method. This method
2279                 * is called from the factory method every time when no resulting
2280                 * resource bundle has been found for <code>baseName</code> and
2281                 * <code>locale</code>, where locale is either the parameter for
2282                 * <code>ResourceBundle.getBundle</code> or the previous fallback
2283                 * locale returned by this method.
2284                 *
2285                 * <p>The method returns <code>null</code> if no further fallback
2286                 * search is desired.
2287                 *
2288                 * <p>The default implementation returns the {@linkplain
2289                 * Locale#getDefault() default <code>Locale</code>} if the given
2290                 * <code>locale</code> isn't the default one.  Otherwise,
2291                 * <code>null</code> is returned.
2292                 *
2293                 * @param baseName
2294                 *        the base name of the resource bundle, a fully
2295                 *        qualified class name for which
2296                 *        <code>ResourceBundle.getBundle</code> has been
2297                 *        unable to find any resource bundles (except for the
2298                 *        base bundle)
2299                 * @param locale
2300                 *        the <code>Locale</code> for which
2301                 *        <code>ResourceBundle.getBundle</code> has been
2302                 *        unable to find any resource bundles (except for the
2303                 *        base bundle)
2304                 * @return a <code>Locale</code> for the fallback search,
2305                 *        or <code>null</code> if no further fallback search
2306                 *        is desired.
2307                 * @exception NullPointerException
2308                 *        if <code>baseName</code> or <code>locale</code>
2309                 *        is <code>null</code>
2310                 */
2311                public Locale getFallbackLocale(String baseName, Locale locale) {
2312                    if (baseName == null) {
2313                        throw new NullPointerException();
2314                    }
2315                    Locale defaultLocale = Locale.getDefault();
2316                    return locale.equals(defaultLocale) ? null : defaultLocale;
2317                }
2318
2319                /**
2320                 * Instantiates a resource bundle for the given bundle name of the
2321                 * given format and locale, using the given class loader if
2322                 * necessary. This method returns <code>null</code> if there is no
2323                 * resource bundle available for the given parameters. If a resource
2324                 * bundle can't be instantiated due to an unexpected error, the
2325                 * error must be reported by throwing an <code>Error</code> or
2326                 * <code>Exception</code> rather than simply returning
2327                 * <code>null</code>.
2328                 *
2329                 * <p>If the <code>reload</code> flag is <code>true</code>, it
2330                 * indicates that this method is being called because the previously
2331                 * loaded resource bundle has expired.
2332                 *
2333                 * <p>The default implementation instantiates a
2334                 * <code>ResourceBundle</code> as follows.
2335                 *
2336                 * <ul>
2337                 *
2338                 * <li>The bundle name is obtained by calling {@link
2339                 * #toBundleName(String, Locale) toBundleName(baseName,
2340                 * locale)}.</li>
2341                 *
2342                 * <li>If <code>format</code> is <code>"java.class"</code>, the
2343                 * {@link Class} specified by the bundle name is loaded by calling
2344                 * {@link ClassLoader#loadClass(String)}. Then, a
2345                 * <code>ResourceBundle</code> is instantiated by calling {@link
2346                 * Class#newInstance()}.  Note that the <code>reload</code> flag is
2347                 * ignored for loading class-based resource bundles in this default
2348                 * implementation.</li>
2349                 *
2350                 * <li>If <code>format</code> is <code>"java.properties"</code>,
2351                 * {@link #toResourceName(String, String) toResourceName(bundlename,
2352                 * "properties")} is called to get the resource name.
2353                 * If <code>reload</code> is <code>true</code>, {@link
2354                 * ClassLoader#getResource(String) load.getResource} is called
2355                 * to get a {@link URL} for creating a {@link
2356                 * URLConnection}. This <code>URLConnection</code> is used to
2357                 * {@linkplain URLConnection#setUseCaches(boolean) disable the
2358                 * caches} of the underlying resource loading layers,
2359                 * and to {@linkplain URLConnection#getInputStream() get an
2360                 * <code>InputStream</code>}.
2361                 * Otherwise, {@link ClassLoader#getResourceAsStream(String)
2362                 * loader.getResourceAsStream} is called to get an {@link
2363                 * InputStream}. Then, a {@link
2364                 * PropertyResourceBundle} is constructed with the
2365                 * <code>InputStream</code>.</li>
2366                 *
2367                 * <li>If <code>format</code> is neither <code>"java.class"</code>
2368                 * nor <code>"java.properties"</code>, an
2369                 * <code>IllegalArgumentException</code> is thrown.</li>
2370                 *
2371                 * </ul>
2372                 *
2373                 * @param baseName
2374                 *        the base bundle name of the resource bundle, a fully
2375                 *        qualified class name
2376                 * @param locale
2377                 *        the locale for which the resource bundle should be
2378                 *        instantiated
2379                 * @param format
2380                 *        the resource bundle format to be loaded
2381                 * @param loader
2382                 *        the <code>ClassLoader</code> to use to load the bundle
2383                 * @param reload
2384                 *        the flag to indicate bundle reloading; <code>true</code>
2385                 *        if reloading an expired resource bundle,
2386                 *        <code>false</code> otherwise
2387                 * @return the resource bundle instance,
2388                 *        or <code>null</code> if none could be found.
2389                 * @exception NullPointerException
2390                 *        if <code>bundleName</code>, <code>locale</code>,
2391                 *        <code>format</code>, or <code>loader</code> is
2392                 *        <code>null</code>, or if <code>null</code> is returned by
2393                 *        {@link #toBundleName(String, Locale) toBundleName}
2394                 * @exception IllegalArgumentException
2395                 *        if <code>format</code> is unknown, or if the resource
2396                 *        found for the given parameters contains malformed data.
2397                 * @exception ClassCastException
2398                 *        if the loaded class cannot be cast to <code>ResourceBundle</code>
2399                 * @exception IllegalAccessException
2400                 *        if the class or its nullary constructor is not
2401                 *        accessible.
2402                 * @exception InstantiationException
2403                 *        if the instantiation of a class fails for some other
2404                 *        reason.
2405                 * @exception ExceptionInInitializerError
2406                 *        if the initialization provoked by this method fails.
2407                 * @exception SecurityException
2408                 *        If a security manager is present and creation of new
2409                 *        instances is denied. See {@link Class#newInstance()}
2410                 *        for details.
2411                 * @exception IOException
2412                 *        if an error occurred when reading resources using
2413                 *        any I/O operations
2414                 */
2415                public ResourceBundle newBundle(String baseName, Locale locale,
2416                        String format, ClassLoader loader, boolean reload)
2417                        throws IllegalAccessException, InstantiationException,
2418                        IOException {
2419                    String bundleName = toBundleName(baseName, locale);
2420                    ResourceBundle bundle = null;
2421                    if (format.equals("java.class")) {
2422                        try {
2423                            Class<? extends ResourceBundle> bundleClass = (Class<? extends ResourceBundle>) loader
2424                                    .loadClass(bundleName);
2425
2426                            // If the class isn't a ResourceBundle subclass, throw a
2427                            // ClassCastException.
2428                            if (ResourceBundle.class
2429                                    .isAssignableFrom(bundleClass)) {
2430                                bundle = bundleClass.newInstance();
2431                            } else {
2432                                throw new ClassCastException(bundleClass
2433                                        .getName()
2434                                        + " cannot be cast to ResourceBundle");
2435                            }
2436                        } catch (ClassNotFoundException e) {
2437                        }
2438                    } else if (format.equals("java.properties")) {
2439                        final String resourceName = toResourceName(bundleName,
2440                                "properties");
2441                        final ClassLoader classLoader = loader;
2442                        final boolean reloadFlag = reload;
2443                        InputStream stream = null;
2444                        try {
2445                            stream = AccessController
2446                                    .doPrivileged(new PrivilegedExceptionAction<InputStream>() {
2447                                        public InputStream run()
2448                                                throws IOException {
2449                                            InputStream is = null;
2450                                            if (reloadFlag) {
2451                                                URL url = classLoader
2452                                                        .getResource(resourceName);
2453                                                if (url != null) {
2454                                                    URLConnection connection = url
2455                                                            .openConnection();
2456                                                    if (connection != null) {
2457                                                        // Disable caches to get fresh data for
2458                                                        // reloading.
2459                                                        connection
2460                                                                .setUseCaches(false);
2461                                                        is = connection
2462                                                                .getInputStream();
2463                                                    }
2464                                                }
2465                                            } else {
2466                                                is = classLoader
2467                                                        .getResourceAsStream(resourceName);
2468                                            }
2469                                            return is;
2470                                        }
2471                                    });
2472                        } catch (PrivilegedActionException e) {
2473                            throw (IOException) e.getException();
2474                        }
2475                        if (stream != null) {
2476                            try {
2477                                bundle = new PropertyResourceBundle(stream);
2478                            } finally {
2479                                stream.close();
2480                            }
2481                        }
2482                    } else {
2483                        throw new IllegalArgumentException("unknown format: "
2484                                + format);
2485                    }
2486                    return bundle;
2487                }
2488
2489                /**
2490                 * Returns the time-to-live (TTL) value for resource bundles that
2491                 * are loaded under this
2492                 * <code>ResourceBundle.Control</code>. Positive time-to-live values
2493                 * specify the number of milliseconds a bundle can remain in the
2494                 * cache without being validated against the source data from which
2495                 * it was constructed. The value 0 indicates that a bundle must be
2496                 * validated each time it is retrieved from the cache. {@link
2497                 * #TTL_DONT_CACHE} specifies that loaded resource bundles are not
2498                 * put in the cache. {@link #TTL_NO_EXPIRATION_CONTROL} specifies
2499                 * that loaded resource bundles are put in the cache with no
2500                 * expiration control.
2501                 *
2502                 * <p>The expiration affects only the bundle loading process by the
2503                 * <code>ResourceBundle.getBundle</code> factory method.  That is,
2504                 * if the factory method finds a resource bundle in the cache that
2505                 * has expired, the factory method calls the {@link
2506                 * #needsReload(String, Locale, String, ClassLoader, ResourceBundle,
2507                 * long) needsReload} method to determine whether the resource
2508                 * bundle needs to be reloaded. If <code>needsReload</code> returns
2509                 * <code>true</code>, the cached resource bundle instance is removed
2510                 * from the cache. Otherwise, the instance stays in the cache,
2511                 * updated with the new TTL value returned by this method.
2512                 *
2513                 * <p>All cached resource bundles are subject to removal from the
2514                 * cache due to memory constraints of the runtime environment.
2515                 * Returning a large positive value doesn't mean to lock loaded
2516                 * resource bundles in the cache.
2517                 *
2518                 * <p>The default implementation returns {@link #TTL_NO_EXPIRATION_CONTROL}.
2519                 *
2520                 * @param baseName
2521                 *	  the base name of the resource bundle for which the
2522                 *	  expiration value is specified.
2523                 * @param locale
2524                 *        the locale of the resource bundle for which the
2525                 *        expiration value is specified.
2526                 * @return the time (0 or a positive millisecond offset from the
2527                 *        cached time) to get loaded bundles expired in the cache,
2528                 *        {@link #TTL_NO_EXPIRATION_CONTROL} to disable the
2529                 *        expiration control, or {@link #TTL_DONT_CACHE} to disable
2530                 *        caching.
2531                 * @exception NullPointerException
2532                 *	  if <code>baseName</code> or <code>locale</code> is
2533                 *	  <code>null</code>
2534                 */
2535                public long getTimeToLive(String baseName, Locale locale) {
2536                    if (baseName == null || locale == null) {
2537                        throw new NullPointerException();
2538                    }
2539                    return TTL_NO_EXPIRATION_CONTROL;
2540                }
2541
2542                /**
2543                 * Determines if the expired <code>bundle</code> in the cache needs
2544                 * to be reloaded based on the loading time given by
2545                 * <code>loadTime</code> or some other criteria. The method returns
2546                 * <code>true</code> if reloading is required; <code>false</code>
2547                 * otherwise. <code>loadTime</code> is a millisecond offset since
2548                 * the <a href="Calendar.html#Epoch"> <code>Calendar</code>
2549                 * Epoch</a>.
2550                 *
2551                 * The calling <code>ResourceBundle.getBundle</code> factory method
2552                 * calls this method on the <code>ResourceBundle.Control</code>
2553                 * instance used for its current invocation, not on the instance
2554                 * used in the invocation that originally loaded the resource
2555                 * bundle.
2556                 *
2557                 * <p>The default implementation compares <code>loadTime</code> and
2558                 * the last modified time of the source data of the resource
2559                 * bundle. If it's determined that the source data has been modified
2560                 * since <code>loadTime</code>, <code>true</code> is
2561                 * returned. Otherwise, <code>false</code> is returned. This
2562                 * implementation assumes that the given <code>format</code> is the
2563                 * same string as its file suffix if it's not one of the default
2564                 * formats, <code>"java.class"</code> or
2565                 * <code>"java.properties"</code>.
2566                 *
2567                 * @param baseName
2568                 *        the base bundle name of the resource bundle, a
2569                 *        fully qualified class name
2570                 * @param locale
2571                 *        the locale for which the resource bundle
2572                 *        should be instantiated
2573                 * @param format
2574                 *        the resource bundle format to be loaded
2575                 * @param loader
2576                 *        the <code>ClassLoader</code> to use to load the bundle
2577                 * @param bundle
2578                 *        the resource bundle instance that has been expired
2579                 *        in the cache
2580                 * @param loadTime
2581                 *        the time when <code>bundle</code> was loaded and put
2582                 *        in the cache
2583                 * @return <code>true</code> if the expired bundle needs to be
2584                 *        reloaded; <code>false</code> otherwise.
2585                 * @exception NullPointerException
2586                 *        if <code>baseName</code>, <code>locale</code>,
2587                 *        <code>format</code>, <code>loader</code>, or
2588                 *        <code>bundle</code> is <code>null</code>
2589                 */
2590                public boolean needsReload(String baseName, Locale locale,
2591                        String format, ClassLoader loader,
2592                        ResourceBundle bundle, long loadTime) {
2593                    if (bundle == null) {
2594                        throw new NullPointerException();
2595                    }
2596                    if (format.equals("java.class")
2597                            || format.equals("java.properties")) {
2598                        format = format.substring(5);
2599                    }
2600                    boolean result = false;
2601                    try {
2602                        String resourceName = toResourceName(toBundleName(
2603                                baseName, locale), format);
2604                        URL url = loader.getResource(resourceName);
2605                        if (url != null) {
2606                            long lastModified = 0;
2607                            URLConnection connection = url.openConnection();
2608                            if (connection != null) {
2609                                // disable caches to get the correct data
2610                                connection.setUseCaches(false);
2611                                if (connection instanceof  JarURLConnection) {
2612                                    JarEntry ent = ((JarURLConnection) connection)
2613                                            .getJarEntry();
2614                                    if (ent != null) {
2615                                        lastModified = ent.getTime();
2616                                        if (lastModified == -1) {
2617                                            lastModified = 0;
2618                                        }
2619                                    }
2620                                } else {
2621                                    lastModified = connection.getLastModified();
2622                                }
2623                            }
2624                            result = lastModified >= loadTime;
2625                        }
2626                    } catch (NullPointerException npe) {
2627                        throw npe;
2628                    } catch (Exception e) {
2629                        // ignore other exceptions
2630                    }
2631                    return result;
2632                }
2633
2634                /**
2635                 * Converts the given <code>baseName</code> and <code>locale</code>
2636                 * to the bundle name. This method is called from the default
2637                 * implementation of the {@link #newBundle(String, Locale, String,
2638                 * ClassLoader, boolean) newBundle} and {@link #needsReload(String,
2639                 * Locale, String, ClassLoader, ResourceBundle, long) needsReload}
2640                 * methods.
2641                 *
2642                 * <p>This implementation returns the following value:
2643                 * <pre>
2644                 *     baseName + "_" + language + "_" + country + "_" + variant
2645                 * </pre>
2646                 * where <code>language</code>, <code>country</code> and
2647                 * <code>variant</code> are the language, country and variant values
2648                 * of <code>locale</code>, respectively. Final component values that
2649                 * are empty Strings are omitted along with the preceding '_'. If
2650                 * all of the values are empty strings, then <code>baseName</code>
2651                 * is returned.
2652                 *
2653                 * <p>For example, if <code>baseName</code> is
2654                 * <code>"baseName"</code> and <code>locale</code> is
2655                 * <code>Locale("ja",&nbsp;"",&nbsp;"XX")</code>, then
2656                 * <code>"baseName_ja_&thinsp;_XX"</code> is returned. If the given
2657                 * locale is <code>Locale("en")</code>, then
2658                 * <code>"baseName_en"</code> is returned.
2659                 *
2660                 * <p>Overriding this method allows applications to use different
2661                 * conventions in the organization and packaging of localized
2662                 * resources.
2663                 *
2664                 * @param baseName
2665                 *        the base name of the resource bundle, a fully
2666                 *        qualified class name
2667                 * @param locale
2668                 *        the locale for which a resource bundle should be
2669                 *        loaded
2670                 * @return the bundle name for the resource bundle
2671                 * @exception NullPointerException
2672                 *        if <code>baseName</code> or <code>locale</code>
2673                 *        is <code>null</code>
2674                 */
2675                public String toBundleName(String baseName, Locale locale) {
2676                    if (locale == Locale.ROOT) {
2677                        return baseName;
2678                    }
2679
2680                    String language = locale.getLanguage();
2681                    String country = locale.getCountry();
2682                    String variant = locale.getVariant();
2683
2684                    if (language == "" && country == "" && variant == "") {
2685                        return baseName;
2686                    }
2687
2688                    StringBuilder sb = new StringBuilder(baseName);
2689                    sb.append('_');
2690                    if (variant != "") {
2691                        sb.append(language).append('_').append(country).append(
2692                                '_').append(variant);
2693                    } else if (country != "") {
2694                        sb.append(language).append('_').append(country);
2695                    } else {
2696                        sb.append(language);
2697                    }
2698                    return sb.toString();
2699
2700                }
2701
2702                /**
2703                 * Converts the given <code>bundleName</code> to the form required
2704                 * by the {@link ClassLoader#getResource ClassLoader.getResource}
2705                 * method by replacing all occurrences of <code>'.'</code> in
2706                 * <code>bundleName</code> with <code>'/'</code> and appending a
2707                 * <code>'.'</code> and the given file <code>suffix</code>. For
2708                 * example, if <code>bundleName</code> is
2709                 * <code>"foo.bar.MyResources_ja_JP"</code> and <code>suffix</code>
2710                 * is <code>"properties"</code>, then
2711                 * <code>"foo/bar/MyResources_ja_JP.properties"</code> is returned.
2712                 *
2713                 * @param bundleName
2714                 *        the bundle name
2715                 * @param suffix
2716                 *        the file type suffix
2717                 * @return the converted resource name
2718                 * @exception NullPointerException
2719                 *         if <code>bundleName</code> or <code>suffix</code>
2720                 *         is <code>null</code>
2721                 */
2722                public final String toResourceName(String bundleName,
2723                        String suffix) {
2724                    StringBuilder sb = new StringBuilder(bundleName.length()
2725                            + 1 + suffix.length());
2726                    sb.append(bundleName.replace('.', '/')).append('.').append(
2727                            suffix);
2728                    return sb.toString();
2729                }
2730            }
2731
2732            private static class SingleFormatControl extends Control {
2733                private static final Control PROPERTIES_ONLY = new SingleFormatControl(
2734                        FORMAT_PROPERTIES);
2735
2736                private static final Control CLASS_ONLY = new SingleFormatControl(
2737                        FORMAT_CLASS);
2738
2739                private final List<String> formats;
2740
2741                protected SingleFormatControl(List<String> formats) {
2742                    this .formats = formats;
2743                }
2744
2745                public List<String> getFormats(String baseName) {
2746                    if (baseName == null) {
2747                        throw new NullPointerException();
2748                    }
2749                    return formats;
2750                }
2751            }
2752
2753            private static final class NoFallbackControl extends
2754                    SingleFormatControl {
2755                private static final Control NO_FALLBACK = new NoFallbackControl(
2756                        FORMAT_DEFAULT);
2757
2758                private static final Control PROPERTIES_ONLY_NO_FALLBACK = new NoFallbackControl(
2759                        FORMAT_PROPERTIES);
2760
2761                private static final Control CLASS_ONLY_NO_FALLBACK = new NoFallbackControl(
2762                        FORMAT_CLASS);
2763
2764                protected NoFallbackControl(List<String> formats) {
2765                    super (formats);
2766                }
2767
2768                public Locale getFallbackLocale(String baseName, Locale locale) {
2769                    if (baseName == null || locale == null) {
2770                        throw new NullPointerException();
2771                    }
2772                    return null;
2773                }
2774            }
2775        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.