2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 import com.ibm.icu4jni.util.ICU;
21 import dalvik.system.VMStack;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
27 import java.net.URLConnection;
28 import java.security.AccessController;
29 import java.security.PrivilegedAction;
32 * {@code ResourceBundle} is an abstract class which is the superclass of classes which
33 * provide {@code Locale}-specific resources. A bundle contains a number of named
34 * resources, where the names are {@code Strings}. A bundle may have a parent bundle,
35 * and when a resource is not found in a bundle, the parent bundle is searched for
36 * the resource. If the fallback mechanism reaches the base bundle and still
37 * can't find the resource it throws a {@code MissingResourceException}.
40 * <li>All bundles for the same group of resources share a common base bundle.
41 * This base bundle acts as the root and is the last fallback in case none of
42 * its children was able to respond to a request.</li>
43 * <li>The first level contains changes between different languages. Only the
44 * differences between a language and the language of the base bundle need to be
45 * handled by a language-specific {@code ResourceBundle}.</li>
46 * <li>The second level contains changes between different countries that use
47 * the same language. Only the differences between a country and the country of
48 * the language bundle need to be handled by a country-specific {@code ResourceBundle}.
50 * <li>The third level contains changes that don't have a geographic reason
51 * (e.g. changes that where made at some point in time like {@code PREEURO} where the
52 * currency of come countries changed. The country bundle would return the
53 * current currency (Euro) and the {@code PREEURO} variant bundle would return the old
54 * currency (e.g. DM for Germany).</li>
57 * <strong>Examples</strong>
59 * <li>BaseName (base bundle)
60 * <li>BaseName_de (german language bundle)
61 * <li>BaseName_fr (french language bundle)
62 * <li>BaseName_de_DE (bundle with Germany specific resources in german)
63 * <li>BaseName_de_CH (bundle with Switzerland specific resources in german)
64 * <li>BaseName_fr_CH (bundle with Switzerland specific resources in french)
65 * <li>BaseName_de_DE_PREEURO (bundle with Germany specific resources in german of
66 * the time before the Euro)
67 * <li>BaseName_fr_FR_PREEURO (bundle with France specific resources in french of
68 * the time before the Euro)
71 * It's also possible to create variants for languages or countries. This can be
72 * done by just skipping the country or language abbreviation:
73 * BaseName_us__POSIX or BaseName__DE_PREEURO. But it's not allowed to
74 * circumvent both language and country: BaseName___VARIANT is illegal.
77 * @see PropertyResourceBundle
78 * @see ListResourceBundle
81 public abstract class ResourceBundle {
83 private static final String UNDER_SCORE = "_";
85 private static final String EMPTY_STRING = "";
88 * The parent of this {@code ResourceBundle} that is used if this bundle doesn't
89 * include the requested resource.
91 protected ResourceBundle parent;
93 private Locale locale;
95 private long lastLoadTime = 0;
97 static class MissingBundle extends ResourceBundle {
99 public Enumeration<String> getKeys() {
104 public Object handleGetObject(String name) {
109 private static final ResourceBundle MISSING = new MissingBundle();
111 private static final ResourceBundle MISSINGBASE = new MissingBundle();
113 private static final WeakHashMap<Object, Hashtable<String, ResourceBundle>> cache = new WeakHashMap<Object, Hashtable<String, ResourceBundle>>();
116 * Constructs a new instance of this class.
118 public ResourceBundle() {
123 * Finds the named resource bundle for the default {@code Locale} and the caller's
124 * {@code ClassLoader}.
127 * the name of the {@code ResourceBundle}.
128 * @return the requested {@code ResourceBundle}.
129 * @throws MissingResourceException
130 * if the {@code ResourceBundle} cannot be found.
132 public static final ResourceBundle getBundle(String bundleName) throws MissingResourceException {
133 return getBundleImpl(bundleName, Locale.getDefault(), VMStack.getCallingClassLoader());
137 * Finds the named {@code ResourceBundle} for the specified {@code Locale} and the caller
138 * {@code ClassLoader}.
141 * the name of the {@code ResourceBundle}.
143 * the {@code Locale}.
144 * @return the requested resource bundle.
145 * @throws MissingResourceException
146 * if the resource bundle cannot be found.
148 public static final ResourceBundle getBundle(String bundleName, Locale locale) {
149 return getBundleImpl(bundleName, locale, VMStack.getCallingClassLoader());
153 * Finds the named resource bundle for the specified {@code Locale} and {@code ClassLoader}.
155 * The passed base name and {@code Locale} are used to create resource bundle names.
156 * The first name is created by concatenating the base name with the result
157 * of {@link Locale#toString()}. From this name all parent bundle names are
158 * derived. Then the same thing is done for the default {@code Locale}. This results
159 * in a list of possible bundle names.
161 * <strong>Example</strong> For the basename "BaseName", the {@code Locale} of the
162 * German part of Switzerland (de_CH) and the default {@code Locale} en_US the list
163 * would look something like this:
166 * <li>BaseName_de_CH</li>
167 * <li>BaseName_de</li>
168 * <li>Basename_en_US</li>
169 * <li>Basename_en</li>
173 * This list also shows the order in which the bundles will be searched for a requested
174 * resource in the German part of Switzerland (de_CH).
176 * As a first step, this method tries to instantiate
177 * a {@code ResourceBundle} with the names provided.
178 * If such a class can be instantiated and initialized, it is returned and
179 * all the parent bundles are instantiated too. If no such class can be
180 * found this method tries to load a {@code .properties} file with the names by
181 * replacing dots in the base name with a slash and by appending
182 * "{@code .properties}" at the end of the string. If such a resource can be found
183 * by calling {@link ClassLoader#getResource(String)} it is used to
184 * initialize a {@link PropertyResourceBundle}. If this succeeds, it will
185 * also load the parents of this {@code ResourceBundle}.
187 * For compatibility with older code, the bundle name isn't required to be
188 * a fully qualified class name. It's also possible to directly pass
189 * the path to a properties file (without a file extension).
192 * the name of the {@code ResourceBundle}.
194 * the {@code Locale}.
196 * the {@code ClassLoader} to use.
197 * @return the requested {@code ResourceBundle}.
198 * @throws MissingResourceException
199 * if the {@code ResourceBundle} cannot be found.
201 public static ResourceBundle getBundle(String bundleName, Locale locale,
202 ClassLoader loader) throws MissingResourceException {
203 if (loader == null) {
204 throw new NullPointerException();
206 if (bundleName != null) {
207 ResourceBundle bundle;
208 if (!locale.equals(Locale.getDefault())) {
209 if ((bundle = handleGetBundle(bundleName, UNDER_SCORE + locale,
210 false, loader)) != null) {
214 if ((bundle = handleGetBundle(bundleName, UNDER_SCORE
215 + Locale.getDefault(), true, loader)) != null) {
218 throw missingResourceException(bundleName + '_' + locale, "");
220 throw new NullPointerException();
223 private static MissingResourceException missingResourceException(String className, String key) {
224 String detail = "Can't find resource for bundle '" + className + "', key '" + key + "'";
225 throw new MissingResourceException(detail, className, key);
229 * Finds the named resource bundle for the specified base name and control.
232 * the base name of a resource bundle
234 * the control that control the access sequence
235 * @return the named resource bundle
239 public static final ResourceBundle getBundle(String baseName, ResourceBundle.Control control) {
240 return getBundle(baseName, Locale.getDefault(), getLoader(), control);
244 * Finds the named resource bundle for the specified base name and control.
247 * the base name of a resource bundle
248 * @param targetLocale
249 * the target locale of the resource bundle
251 * the control that control the access sequence
252 * @return the named resource bundle
256 public static final ResourceBundle getBundle(String baseName,
257 Locale targetLocale, ResourceBundle.Control control) {
258 return getBundle(baseName, targetLocale, getLoader(), control);
261 private static ClassLoader getLoader() {
262 return AccessController
263 .doPrivileged(new PrivilegedAction<ClassLoader>() {
264 public ClassLoader run() {
265 ClassLoader cl = this.getClass().getClassLoader();
267 cl = ClassLoader.getSystemClassLoader();
275 * Finds the named resource bundle for the specified base name and control.
278 * the base name of a resource bundle
279 * @param targetLocale
280 * the target locale of the resource bundle
282 * the class loader to load resource
284 * the control that control the access sequence
285 * @return the named resource bundle
289 public static ResourceBundle getBundle(String baseName,
290 Locale targetLocale, ClassLoader loader,
291 ResourceBundle.Control control) {
292 boolean expired = false;
293 String bundleName = control.toBundleName(baseName, targetLocale);
294 Object cacheKey = loader != null ? (Object) loader : (Object) "null";
295 Hashtable<String, ResourceBundle> loaderCache;
296 // try to find in cache
297 synchronized (cache) {
298 loaderCache = cache.get(cacheKey);
299 if (loaderCache == null) {
300 loaderCache = new Hashtable<String, ResourceBundle>();
301 cache.put(cacheKey, loaderCache);
304 ResourceBundle result = loaderCache.get(bundleName);
305 if (result != null) {
306 long time = control.getTimeToLive(baseName, targetLocale);
307 if (time == 0 || time == Control.TTL_NO_EXPIRATION_CONTROL
308 || time + result.lastLoadTime < System.currentTimeMillis()) {
309 if (MISSING == result) {
310 throw new MissingResourceException(null, bundleName + '_'
311 + targetLocale, EMPTY_STRING);
318 ResourceBundle ret = processGetBundle(baseName, targetLocale, loader,
319 control, expired, result);
322 loaderCache.put(bundleName, ret);
323 ret.lastLoadTime = System.currentTimeMillis();
326 loaderCache.put(bundleName, MISSING);
327 throw new MissingResourceException(null, bundleName + '_'
328 + targetLocale, EMPTY_STRING);
331 private static ResourceBundle processGetBundle(String baseName,
332 Locale targetLocale, ClassLoader loader,
333 ResourceBundle.Control control, boolean expired,
334 ResourceBundle result) {
335 List<Locale> locales = control.getCandidateLocales(baseName,
337 if (null == locales) {
338 throw new IllegalArgumentException();
340 List<String> formats = control.getFormats(baseName);
341 if (Control.FORMAT_CLASS == formats
342 || Control.FORMAT_PROPERTIES == formats
343 || Control.FORMAT_DEFAULT == formats) {
344 throw new IllegalArgumentException();
346 ResourceBundle ret = null;
347 ResourceBundle currentBundle = null;
348 ResourceBundle bundle = null;
349 for (Locale locale : locales) {
350 for (String format : formats) {
353 bundle = control.newBundle(baseName, locale, format,
354 loader, control.needsReload(baseName, locale,
355 format, loader, result, System
356 .currentTimeMillis()));
360 bundle = control.newBundle(baseName, locale,
361 format, loader, false);
362 } catch (IllegalArgumentException e) {
366 } catch (IllegalAccessException e) {
368 } catch (InstantiationException e) {
370 } catch (IOException e) {
373 if (null != bundle) {
374 if (null != currentBundle) {
375 currentBundle.setParent(bundle);
376 currentBundle = bundle;
384 if (null != bundle) {
391 || (Locale.ROOT.equals(ret.getLocale()) && (!(locales.size() == 1 && locales
392 .contains(Locale.ROOT))))) {
393 Locale nextLocale = control.getFallbackLocale(baseName,
395 if (null != nextLocale) {
396 ret = processGetBundle(baseName, nextLocale, loader, control,
404 private static ResourceBundle getBundleImpl(String bundleName,
405 Locale locale, ClassLoader loader) throws MissingResourceException {
406 if (bundleName != null) {
407 ResourceBundle bundle;
408 if (!locale.equals(Locale.getDefault())) {
409 String localeName = locale.toString();
410 if (localeName.length() > 0) {
411 localeName = UNDER_SCORE + localeName;
413 if ((bundle = handleGetBundle(bundleName, localeName, false,
418 String localeName = Locale.getDefault().toString();
419 if (localeName.length() > 0) {
420 localeName = UNDER_SCORE + localeName;
422 if ((bundle = handleGetBundle(bundleName, localeName, true, loader)) != null) {
425 throw missingResourceException(bundleName + '_' + locale, "");
427 throw new NullPointerException();
431 * Returns the names of the resources contained in this {@code ResourceBundle}.
433 * @return an {@code Enumeration} of the resource names.
435 public abstract Enumeration<String> getKeys();
438 * Gets the {@code Locale} of this {@code ResourceBundle}. In case a bundle was not
439 * found for the requested {@code Locale}, this will return the actual {@code Locale} of
440 * this resource bundle that was found after doing a fallback.
442 * @return the {@code Locale} of this {@code ResourceBundle}.
444 public Locale getLocale() {
449 * Returns the named resource from this {@code ResourceBundle}. If the resource
450 * cannot be found in this bundle, it falls back to the parent bundle (if
451 * it's not null) by calling the {@link #handleGetObject} method. If the resource still
452 * can't be found it throws a {@code MissingResourceException}.
455 * the name of the resource.
456 * @return the resource object.
457 * @throws MissingResourceException
458 * if the resource is not found.
460 public final Object getObject(String key) {
461 ResourceBundle last, theParent = this;
463 Object result = theParent.handleGetObject(key);
464 if (result != null) {
468 theParent = theParent.parent;
469 } while (theParent != null);
470 throw missingResourceException(last.getClass().getName(), key);
474 * Returns the named string resource from this {@code ResourceBundle}.
477 * the name of the resource.
478 * @return the resource string.
479 * @throws MissingResourceException
480 * if the resource is not found.
481 * @throws ClassCastException
482 * if the resource found is not a string.
483 * @see #getObject(String)
485 public final String getString(String key) {
486 return (String) getObject(key);
490 * Returns the named resource from this {@code ResourceBundle}.
493 * the name of the resource.
494 * @return the resource string array.
495 * @throws MissingResourceException
496 * if the resource is not found.
497 * @throws ClassCastException
498 * if the resource found is not an array of strings.
499 * @see #getObject(String)
501 public final String[] getStringArray(String key) {
502 return (String[]) getObject(key);
505 private static ResourceBundle handleGetBundle(String base, String locale,
506 boolean loadBase, final ClassLoader loader) {
507 ResourceBundle bundle = null;
508 String bundleName = base + locale;
509 Object cacheKey = loader != null ? (Object) loader : (Object) "null";
510 Hashtable<String, ResourceBundle> loaderCache;
511 synchronized (cache) {
512 loaderCache = cache.get(cacheKey);
513 if (loaderCache == null) {
514 loaderCache = new Hashtable<String, ResourceBundle>();
515 cache.put(cacheKey, loaderCache);
518 ResourceBundle result = loaderCache.get(bundleName);
519 if (result != null) {
520 if (result == MISSINGBASE) {
523 if (result == MISSING) {
527 String extension = strip(locale);
528 if (extension == null) {
531 return handleGetBundle(base, extension, loadBase, loader);
537 Class<?> bundleClass = Class.forName(bundleName, true, loader);
539 if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
540 bundle = (ResourceBundle) bundleClass.newInstance();
542 } catch (LinkageError e) {
543 } catch (Exception e) {
546 if (bundle != null) {
547 bundle.setLocale(locale);
549 final String fileName = bundleName.replace('.', '/');
550 InputStream stream = AccessController
551 .doPrivileged(new PrivilegedAction<InputStream>() {
552 public InputStream run() {
553 return loader == null ? ClassLoader
554 .getSystemResourceAsStream(fileName
555 + ".properties") : loader
556 .getResourceAsStream(fileName
560 if (stream != null) {
563 bundle = new PropertyResourceBundle(new InputStreamReader(stream));
567 bundle.setLocale(locale);
568 } catch (IOException e) {
574 String extension = strip(locale);
575 if (bundle != null) {
576 if (extension != null) {
577 ResourceBundle parent = handleGetBundle(base, extension, true,
579 if (parent != null) {
580 bundle.setParent(parent);
583 loaderCache.put(bundleName, bundle);
587 if (extension != null && (loadBase || extension.length() > 0)) {
588 bundle = handleGetBundle(base, extension, loadBase, loader);
589 if (bundle != null) {
590 loaderCache.put(bundleName, bundle);
594 loaderCache.put(bundleName, loadBase ? MISSINGBASE : MISSING);
599 * Returns the named resource from this {@code ResourceBundle}, or null if the
600 * resource is not found.
603 * the name of the resource.
604 * @return the resource object.
606 protected abstract Object handleGetObject(String key);
609 * Sets the parent resource bundle of this {@code ResourceBundle}. The parent is
610 * searched for resources which are not found in this {@code ResourceBundle}.
613 * the parent {@code ResourceBundle}.
615 protected void setParent(ResourceBundle bundle) {
619 private static String strip(String name) {
620 int index = name.lastIndexOf('_');
622 return name.substring(0, index);
627 private void setLocale(Locale locale) {
628 this.locale = locale;
631 private void setLocale(String name) {
632 setLocale(ICU.localeFromString(name));
635 public static final void clearCache() {
636 cache.remove(ClassLoader.getSystemClassLoader());
639 public static final void clearCache(ClassLoader loader) {
640 if (null == loader) {
641 throw new NullPointerException();
643 cache.remove(loader);
646 public boolean containsKey(String key) {
648 throw new NullPointerException();
650 return keySet().contains(key);
653 public Set<String> keySet() {
654 Set<String> ret = new HashSet<String>();
655 Enumeration<String> keys = getKeys();
656 while (keys.hasMoreElements()) {
657 ret.add(keys.nextElement());
662 protected Set<String> handleKeySet() {
663 Set<String> set = keySet();
664 Set<String> ret = new HashSet<String>();
665 for (String key : set) {
666 if (null != handleGetObject(key)) {
673 private static class NoFallbackControl extends Control {
675 static final Control NOFALLBACK_FORMAT_PROPERTIES_CONTROL = new NoFallbackControl(
678 static final Control NOFALLBACK_FORMAT_CLASS_CONTROL = new NoFallbackControl(
681 static final Control NOFALLBACK_FORMAT_DEFAULT_CONTROL = new NoFallbackControl(
684 public NoFallbackControl(String format) {
686 listClass = new ArrayList<String>();
687 listClass.add(format);
688 super.format = Collections.unmodifiableList(listClass);
691 public NoFallbackControl(List<String> list) {
697 public Locale getFallbackLocale(String baseName, Locale locale) {
698 if (null == baseName || null == locale) {
699 throw new NullPointerException();
705 private static class SimpleControl extends Control {
706 public SimpleControl(String format) {
708 listClass = new ArrayList<String>();
709 listClass.add(format);
710 super.format = Collections.unmodifiableList(listClass);
714 @SuppressWarnings("nls")
716 * ResourceBundle.Control is a static utility class defines ResourceBundle
717 * load access methods, its default access order is as the same as before.
718 * However users can implement their own control.
722 public static class Control {
723 static List<String> listDefault = new ArrayList<String>();
725 static List<String> listClass = new ArrayList<String>();
727 static List<String> listProperties = new ArrayList<String>();
729 static String JAVACLASS = "java.class";
731 static String JAVAPROPERTIES = "java.properties";
734 listDefault.add(JAVACLASS);
735 listDefault.add(JAVAPROPERTIES);
736 listClass.add(JAVACLASS);
737 listProperties.add(JAVAPROPERTIES);
741 * a list defines default format
743 public static final List<String> FORMAT_DEFAULT = Collections
744 .unmodifiableList(listDefault);
747 * a list defines java class format
749 public static final List<String> FORMAT_CLASS = Collections
750 .unmodifiableList(listClass);
753 * a list defines property format
755 public static final List<String> FORMAT_PROPERTIES = Collections
756 .unmodifiableList(listProperties);
759 * a constant that indicates cache will not be used.
761 public static final long TTL_DONT_CACHE = -1L;
764 * a constant that indicates cache will not be expired.
766 public static final long TTL_NO_EXPIRATION_CONTROL = -2L;
768 private static final Control FORMAT_PROPERTIES_CONTROL = new SimpleControl(
771 private static final Control FORMAT_CLASS_CONTROL = new SimpleControl(
774 private static final Control FORMAT_DEFAULT_CONTROL = new Control();
779 * default constructor
782 protected Control() {
784 listClass = new ArrayList<String>();
785 listClass.add(JAVACLASS);
786 listClass.add(JAVAPROPERTIES);
787 format = Collections.unmodifiableList(listClass);
791 * Returns a control according to {@code formats}.
793 public static final Control getControl(List<String> formats) {
794 switch (formats.size()) {
796 if (formats.contains(JAVACLASS)) {
797 return FORMAT_CLASS_CONTROL;
799 if (formats.contains(JAVAPROPERTIES)) {
800 return FORMAT_PROPERTIES_CONTROL;
804 if (formats.equals(FORMAT_DEFAULT)) {
805 return FORMAT_DEFAULT_CONTROL;
809 throw new IllegalArgumentException();
813 * Returns a control according to {@code formats} whose fallback
816 public static final Control getNoFallbackControl(List<String> formats) {
817 switch (formats.size()) {
819 if (formats.contains(JAVACLASS)) {
820 return NoFallbackControl.NOFALLBACK_FORMAT_CLASS_CONTROL;
822 if (formats.contains(JAVAPROPERTIES)) {
823 return NoFallbackControl.NOFALLBACK_FORMAT_PROPERTIES_CONTROL;
827 if (formats.equals(FORMAT_DEFAULT)) {
828 return NoFallbackControl.NOFALLBACK_FORMAT_DEFAULT_CONTROL;
832 throw new IllegalArgumentException();
836 * Returns a list of candidate locales according to {@code baseName} in
839 public List<Locale> getCandidateLocales(String baseName, Locale locale) {
840 if (null == baseName || null == locale) {
841 throw new NullPointerException();
843 List<Locale> retList = new ArrayList<Locale>();
844 String language = locale.getLanguage();
845 String country = locale.getCountry();
846 String variant = locale.getVariant();
847 if (!EMPTY_STRING.equals(variant)) {
848 retList.add(new Locale(language, country, variant));
850 if (!EMPTY_STRING.equals(country)) {
851 retList.add(new Locale(language, country));
853 if (!EMPTY_STRING.equals(language)) {
854 retList.add(new Locale(language));
856 retList.add(Locale.ROOT);
861 * Returns a list of strings of formats according to {@code baseName}.
863 public List<String> getFormats(String baseName) {
864 if (null == baseName) {
865 throw new NullPointerException();
871 * Returns the fallback locale for {@code baseName} in {@code locale}.
873 public Locale getFallbackLocale(String baseName, Locale locale) {
874 if (null == baseName || null == locale) {
875 throw new NullPointerException();
877 if (Locale.getDefault() != locale) {
878 return Locale.getDefault();
884 * Returns a new ResourceBundle.
887 * the base name to use
891 * the format, default is "java.class" or "java.properties"
893 * the classloader to use
895 * whether to reload the resource
896 * @return a new ResourceBundle according to the give parameters
897 * @throws IllegalAccessException
898 * if we can not access resources
899 * @throws InstantiationException
900 * if we can not instantiate a resource class
901 * @throws IOException
902 * if other I/O exception happens
904 public ResourceBundle newBundle(String baseName, Locale locale,
905 String format, ClassLoader loader, boolean reload)
906 throws IllegalAccessException, InstantiationException,
908 if (null == format || null == loader) {
909 throw new NullPointerException();
911 InputStream streams = null;
912 final String bundleName = toBundleName(baseName, locale);
913 final ClassLoader clsloader = loader;
916 if (JAVACLASS == format) {
917 cls = AccessController
918 .doPrivileged(new PrivilegedAction<Class<?>>() {
919 public Class<?> run() {
921 return clsloader.loadClass(bundleName);
922 } catch (Exception e) {
924 } catch (NoClassDefFoundError e) {
933 ResourceBundle bundle = (ResourceBundle) cls.newInstance();
934 bundle.setLocale(locale);
936 } catch (NullPointerException e) {
940 if (JAVAPROPERTIES == format) {
941 final String resourceName = toResourceName(bundleName,
946 url = loader.getResource(resourceName);
947 } catch (NullPointerException e) {
951 URLConnection con = url.openConnection();
952 con.setUseCaches(false);
953 streams = con.getInputStream();
957 streams = AccessController
958 .doPrivileged(new PrivilegedAction<InputStream>() {
959 public InputStream run() {
961 .getResourceAsStream(resourceName);
964 } catch (NullPointerException e) {
968 if (streams != null) {
970 ret = new PropertyResourceBundle(new InputStreamReader(streams));
971 ret.setLocale(locale);
973 } catch (IOException e) {
980 throw new IllegalArgumentException();
984 * Returns the time to live of the ResourceBundle {@code baseName} in {@code locale},
985 * default is TTL_NO_EXPIRATION_CONTROL.
987 public long getTimeToLive(String baseName, Locale locale) {
988 if (null == baseName || null == locale) {
989 throw new NullPointerException();
991 return TTL_NO_EXPIRATION_CONTROL;
995 * Returns true if the ResourceBundle needs to reload.
998 * the base name of the ResourceBundle
1000 * the locale of the ResourceBundle
1002 * the format to load
1004 * the ClassLoader to load resource
1006 * the ResourceBundle
1009 * @return if the ResourceBundle needs to reload
1011 public boolean needsReload(String baseName, Locale locale,
1012 String format, ClassLoader loader, ResourceBundle bundle,
1014 if (null == bundle) {
1015 // FIXME what's the use of bundle?
1016 throw new NullPointerException();
1018 String bundleName = toBundleName(baseName, locale);
1019 String suffix = format;
1020 if (JAVACLASS == format) {
1023 if (JAVAPROPERTIES == format) {
1024 suffix = "properties";
1026 String urlname = toResourceName(bundleName, suffix);
1027 URL url = loader.getResource(urlname);
1029 String fileName = url.getFile();
1030 long lastModified = new File(fileName).lastModified();
1031 if (lastModified > loadTime) {
1039 * a utility method to answer the name of a resource bundle according to
1040 * the given base name and locale
1043 * the given base name
1046 * @return the name of a resource bundle according to the given base
1049 public String toBundleName(String baseName, Locale locale) {
1050 final String emptyString = EMPTY_STRING;
1051 final String preString = UNDER_SCORE;
1052 final String underline = UNDER_SCORE;
1053 if (null == baseName) {
1054 throw new NullPointerException();
1056 StringBuilder ret = new StringBuilder();
1057 StringBuilder prefix = new StringBuilder();
1058 ret.append(baseName);
1059 if (!locale.getLanguage().equals(emptyString)) {
1060 ret.append(underline);
1061 ret.append(locale.getLanguage());
1063 prefix.append(preString);
1065 if (!locale.getCountry().equals(emptyString)) {
1066 ret.append((CharSequence) prefix);
1067 ret.append(underline);
1068 ret.append(locale.getCountry());
1069 prefix = new StringBuilder();
1071 prefix.append(preString);
1073 if (!locale.getVariant().equals(emptyString)) {
1074 ret.append((CharSequence) prefix);
1075 ret.append(underline);
1076 ret.append(locale.getVariant());
1078 return ret.toString();
1082 * a utility method to answer the name of a resource according to the
1083 * given bundleName and suffix
1086 * the given bundle name
1089 * @return the name of a resource according to the given bundleName and
1092 public final String toResourceName(String bundleName, String suffix) {
1093 if (null == suffix) {
1094 throw new NullPointerException();
1096 StringBuilder ret = new StringBuilder(bundleName.replace('.', '/'));
1099 return ret.toString();