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

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 geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
PHP
Python
SQL Server / T-SQL
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
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:        }
w___w_w___.j___av_a___2__s.__c__om_ | Contact Us
Copyright 2003 - 08 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.