2 *******************************************************************************
\r
3 * Copyright (C) 2004-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
8 package com.ibm.icu.util;
\r
10 import java.lang.ref.SoftReference;
\r
11 import java.nio.ByteBuffer;
\r
12 import java.util.Collections;
\r
13 import java.util.Enumeration;
\r
14 import java.util.HashMap;
\r
15 import java.util.Locale;
\r
16 import java.util.Map;
\r
17 import java.util.MissingResourceException;
\r
18 import java.util.ResourceBundle;
\r
19 import java.util.Set;
\r
20 import java.util.TreeSet;
\r
22 import com.ibm.icu.impl.ICUCache;
\r
23 import com.ibm.icu.impl.ICUResourceBundle;
\r
24 import com.ibm.icu.impl.ResourceBundleWrapper;
\r
25 import com.ibm.icu.impl.SimpleCache;
\r
28 * {@icuenhanced java.util.ResourceBundle}.{@icu _usage_}
\r
30 * <p>A class representing a collection of resource information pertaining to a given
\r
31 * locale. A resource bundle provides a way of accessing locale- specific information in a
\r
32 * data file. You create a resource bundle that manages the resources for a given locale
\r
33 * and then ask it for individual resources.
\r
35 * <p>In ResourceBundle, an object is created and the sub-items are fetched using the
\r
36 * getString and getObject methods. In UResourceBundle, each individual element of a
\r
37 * resource is a resource by itself.
\r
39 * <p>Resource bundles in ICU are currently defined using text files that conform to the
\r
41 * href="http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt">BNF
\r
42 * definition</a>. More on resource bundle concepts and syntax can be found in the <a
\r
43 * href="http://www.icu-project.org/userguide/ResourceManagement.html">Users Guide</a>.
\r
45 * <p>The packaging of ICU *.res files can be of two types
\r
56 * fr_CA.res fr_FR.res
\r
60 * LocaleElements.res
\r
62 * -------------------
\r
64 * LocaleElements_fr.res LocaleElements_en.res
\r
66 * ---------------------------
\r
68 * LocaleElements_fr_CA.res LocaleElements_fr_FR.res
\r
71 * Depending on the organization of your resources, the syntax to getBundleInstance will
\r
72 * change. To open ICU style organization use:
\r
75 * UResourceBundle bundle =
\r
76 * UResourceBundle.getBundleInstance("com/mycompany/resources",
\r
77 * "en_US", myClassLoader);
\r
79 * To open Java/JDK style organization use:
\r
81 * UResourceBundle bundle =
\r
82 * UResourceBundle.getBundleInstance("com.mycompany.resources.LocaleElements",
\r
83 * "en_US", myClassLoader);
\r
86 * Please use pass a class loader for loading non-ICU resources. Java security does not
\r
87 * allow loading of resources across jar files. You must provide your class loader
\r
88 * to load the resources
\r
93 public abstract class UResourceBundle extends ResourceBundle {
\r
97 * {@icu} Creates a resource bundle using the specified base name and locale.
\r
98 * ICU_DATA_CLASS is used as the default root.
\r
99 * @param baseName the base name of the resource bundle, a fully qualified class name
\r
100 * @param localeName the locale for which a resource bundle is desired
\r
101 * @throws MissingResourceException If no resource bundle for the specified base name
\r
103 * @return a resource bundle for the given base name and locale
\r
106 public static UResourceBundle getBundleInstance(String baseName, String localeName){
\r
107 return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER,
\r
112 * {@icu} Creates a resource bundle using the specified base name, locale, and class root.
\r
114 * @param baseName the base name of the resource bundle, a fully qualified class name
\r
115 * @param localeName the locale for which a resource bundle is desired
\r
116 * @param root the class object from which to load the resource bundle
\r
117 * @throws MissingResourceException If no resource bundle for the specified base name
\r
119 * @return a resource bundle for the given base name and locale
\r
122 public static UResourceBundle getBundleInstance(String baseName, String localeName,
\r
124 return getBundleInstance(baseName, localeName, root, false);
\r
128 * {@icu} Creates a resource bundle using the specified base name, locale, and class
\r
131 * @param baseName the base name of the resource bundle, a fully qualified class name
\r
132 * @param localeName the locale for which a resource bundle is desired
\r
133 * @param root the class object from which to load the resource bundle
\r
134 * @param disableFallback Option to disable locale inheritence.
\r
135 * If true the fallback chain will not be built.
\r
136 * @throws MissingResourceException
\r
137 * if no resource bundle for the specified base name can be found
\r
138 * @return a resource bundle for the given base name and locale
\r
142 protected static UResourceBundle getBundleInstance(String baseName, String localeName,
\r
143 ClassLoader root, boolean disableFallback) {
\r
144 return instantiateBundle(baseName, localeName, root, disableFallback);
\r
148 * {@icu} Sole constructor. (For invocation by subclass constructors, typically
\r
149 * implicit.) This is public for compatibility with Java, whose compiler
\r
150 * will generate public default constructors for an abstract class.
\r
153 public UResourceBundle() {
\r
157 * {@icu} Creates a UResourceBundle for the locale specified, from which users can extract
\r
158 * resources by using their corresponding keys.
\r
159 * @param locale specifies the locale for which we want to open the resource.
\r
160 * If null the bundle for default locale is opened.
\r
161 * @return a resource bundle for the given locale
\r
164 public static UResourceBundle getBundleInstance(ULocale locale) {
\r
165 if (locale==null) {
\r
166 locale = ULocale.getDefault();
\r
168 return getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale.toString(),
\r
169 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
\r
173 * {@icu} Creates a UResourceBundle for the default locale and specified base name,
\r
174 * from which users can extract resources by using their corresponding keys.
\r
175 * @param baseName specifies the locale for which we want to open the resource.
\r
176 * If null the bundle for default locale is opened.
\r
177 * @return a resource bundle for the given base name and default locale
\r
180 public static UResourceBundle getBundleInstance(String baseName) {
\r
181 if (baseName == null) {
\r
182 baseName = ICUResourceBundle.ICU_BASE_NAME;
\r
184 ULocale uloc = ULocale.getDefault();
\r
185 return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER,
\r
190 * {@icu} Creates a UResourceBundle for the specified locale and specified base name,
\r
191 * from which users can extract resources by using their corresponding keys.
\r
192 * @param baseName specifies the locale for which we want to open the resource.
\r
193 * If null the bundle for default locale is opened.
\r
194 * @param locale specifies the locale for which we want to open the resource.
\r
195 * If null the bundle for default locale is opened.
\r
196 * @return a resource bundle for the given base name and locale
\r
200 public static UResourceBundle getBundleInstance(String baseName, Locale locale) {
\r
201 if (baseName == null) {
\r
202 baseName = ICUResourceBundle.ICU_BASE_NAME;
\r
204 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
\r
206 return getBundleInstance(baseName, uloc.toString(), ICUResourceBundle.ICU_DATA_CLASS_LOADER,
\r
211 * {@icu} Creates a UResourceBundle, from which users can extract resources by using
\r
212 * their corresponding keys.
\r
213 * @param baseName string containing the name of the data package.
\r
214 * If null the default ICU package name is used.
\r
215 * @param locale specifies the locale for which we want to open the resource.
\r
216 * If null the bundle for default locale is opened.
\r
217 * @return a resource bundle for the given base name and locale
\r
220 public static UResourceBundle getBundleInstance(String baseName, ULocale locale) {
\r
221 if (baseName == null) {
\r
222 baseName = ICUResourceBundle.ICU_BASE_NAME;
\r
224 if (locale == null) {
\r
225 locale = ULocale.getDefault();
\r
227 return getBundleInstance(baseName, locale.toString(),
\r
228 ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
\r
232 * {@icu} Creates a UResourceBundle for the specified locale and specified base name,
\r
233 * from which users can extract resources by using their corresponding keys.
\r
234 * @param baseName specifies the locale for which we want to open the resource.
\r
235 * If null the bundle for default locale is opened.
\r
236 * @param locale specifies the locale for which we want to open the resource.
\r
237 * If null the bundle for default locale is opened.
\r
238 * @param loader the loader to use
\r
239 * @return a resource bundle for the given base name and locale
\r
242 public static UResourceBundle getBundleInstance(String baseName, Locale locale,
\r
243 ClassLoader loader) {
\r
244 if (baseName == null) {
\r
245 baseName = ICUResourceBundle.ICU_BASE_NAME;
\r
247 ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
\r
248 return getBundleInstance(baseName, uloc.toString(), loader, false);
\r
252 * {@icu} Creates a UResourceBundle, from which users can extract resources by using
\r
253 * their corresponding keys.<br><br>
\r
254 * Note: Please use this API for loading non-ICU resources. Java security does not
\r
255 * allow loading of resources across jar files. You must provide your class loader
\r
256 * to load the resources
\r
257 * @param baseName string containing the name of the data package.
\r
258 * If null the default ICU package name is used.
\r
259 * @param locale specifies the locale for which we want to open the resource.
\r
260 * If null the bundle for default locale is opened.
\r
261 * @param loader the loader to use
\r
262 * @return a resource bundle for the given base name and locale
\r
265 public static UResourceBundle getBundleInstance(String baseName, ULocale locale,
\r
266 ClassLoader loader) {
\r
267 if (baseName == null) {
\r
268 baseName = ICUResourceBundle.ICU_BASE_NAME;
\r
270 if (locale == null) {
\r
271 locale = ULocale.getDefault();
\r
273 return getBundleInstance(baseName, locale.toString(), loader, false);
\r
277 * {@icu} Returns the RFC 3066 conformant locale id of this resource bundle.
\r
278 * This method can be used after a call to getBundleInstance() to
\r
279 * determine whether the resource bundle returned really
\r
280 * corresponds to the requested locale or is a fallback.
\r
282 * @return the locale of this resource bundle
\r
285 public abstract ULocale getULocale();
\r
288 * {@icu} Returns the localeID
\r
289 * @return The string representation of the localeID
\r
292 protected abstract String getLocaleID();
\r
295 * {@icu} Returns the base name of the resource bundle
\r
296 * @return The string representation of the base name
\r
299 protected abstract String getBaseName();
\r
302 * {@icu} Returns the parent bundle
\r
303 * @return The parent bundle
\r
306 protected abstract UResourceBundle getParent();
\r
310 * Returns the locale of this bundle
\r
311 * @return the locale of this resource bundle
\r
314 public Locale getLocale(){
\r
315 return getULocale().toLocale();
\r
318 // Cache for ResourceBundle instantiation
\r
319 private static ICUCache<ResourceCacheKey, UResourceBundle> BUNDLE_CACHE =
\r
320 new SimpleCache<ResourceCacheKey, UResourceBundle>();
\r
324 * @deprecated This API is ICU internal only.
\r
326 public static void resetBundleCache() {
\r
329 * Currently if a resourcebundle with fallback turned ON is added to the cache
\r
330 * and then a getBundleInstance() is called for a bundle with fallback turned OFF
\r
331 * it will actually search the cache for any bundle of the same locale
\r
332 * regaurdless of fallback status. This method has been created so that if
\r
333 * The calling method KNOWS that instances of the other fallback state may be in the
\r
334 * cache, the calling method may call this method to clear out the cache.
\r
337 //TODO figure a way around this method(see method comment)
\r
338 BUNDLE_CACHE = new SimpleCache<ResourceCacheKey, UResourceBundle>();
\r
342 * Method used by subclasses to add a resource bundle object to the managed
\r
343 * cache. Works like a putIfAbsent(): If the cache already contains a matching
\r
344 * bundle, that one will be retained and returned.
\r
346 * @deprecated This API is ICU internal only.
\r
348 protected static UResourceBundle addToCache(ClassLoader cl, String fullName,
\r
349 ULocale defaultLocale, UResourceBundle b) {
\r
350 synchronized(cacheKey){
\r
351 cacheKey.setKeyValues(cl, fullName, defaultLocale);
\r
352 UResourceBundle cachedBundle = BUNDLE_CACHE.get(cacheKey);
\r
353 if (cachedBundle != null) {
\r
354 return cachedBundle;
\r
356 BUNDLE_CACHE.put((ResourceCacheKey)cacheKey.clone(), b);
\r
362 * Method used by sub classes to load a resource bundle object from the managed cache
\r
364 * @deprecated This API is ICU internal only.
\r
366 protected static UResourceBundle loadFromCache(ClassLoader cl, String fullName,
\r
367 ULocale defaultLocale){
\r
368 synchronized(cacheKey){
\r
369 cacheKey.setKeyValues(cl, fullName, defaultLocale);
\r
370 return BUNDLE_CACHE.get(cacheKey);
\r
375 * Key used for cached resource bundles. The key checks
\r
376 * the resource name, the class root, and the default
\r
377 * locale to determine if the resource is a match to the
\r
378 * requested one. The root may be null, but the
\r
379 * searchName and the default locale must have a non-null value.
\r
380 * Note that the default locale may change over time, and
\r
381 * lookup should always be based on the current default
\r
382 * locale (if at all).
\r
384 private static final class ResourceCacheKey implements Cloneable {
\r
385 private SoftReference<ClassLoader> loaderRef;
\r
386 private String searchName;
\r
387 private ULocale defaultLocale;
\r
388 private int hashCodeCache;
\r
390 public boolean equals(Object other) {
\r
391 if (this == other) {
\r
395 final ResourceCacheKey otherEntry = (ResourceCacheKey) other;
\r
396 //quick check to see if they are not equal
\r
397 if (hashCodeCache != otherEntry.hashCodeCache) {
\r
400 //are the names the same?
\r
401 if (!searchName.equals(otherEntry.searchName)) {
\r
404 // are the default locales the same?
\r
405 if (defaultLocale == null) {
\r
406 if (otherEntry.defaultLocale != null) {
\r
410 if (!defaultLocale.equals(otherEntry.defaultLocale)) {
\r
414 //are refs (both non-null) or (both null)?
\r
415 if (loaderRef == null) {
\r
416 return otherEntry.loaderRef == null;
\r
418 return (otherEntry.loaderRef != null)
\r
419 && (loaderRef.get() == otherEntry.loaderRef.get());
\r
421 } catch (NullPointerException e) {
\r
423 } catch (ClassCastException e) {
\r
428 public int hashCode() {
\r
429 return hashCodeCache;
\r
432 public Object clone() {
\r
434 return super.clone();
\r
435 } catch (CloneNotSupportedException e) {
\r
436 //this should never happen
\r
437 throw new IllegalStateException();
\r
442 private synchronized void setKeyValues(ClassLoader root, String searchName,
\r
443 ULocale defaultLocale) {
\r
444 this.searchName = searchName;
\r
445 hashCodeCache = searchName.hashCode();
\r
446 this.defaultLocale = defaultLocale;
\r
447 if (defaultLocale != null) {
\r
448 hashCodeCache ^= defaultLocale.hashCode();
\r
450 if (root == null) {
\r
451 this.loaderRef = null;
\r
453 loaderRef = new SoftReference<ClassLoader>(root);
\r
454 hashCodeCache ^= root.hashCode();
\r
457 /*private void clear() {
\r
458 setKeyValues(null, "", null);
\r
462 private static final ResourceCacheKey cacheKey = new ResourceCacheKey();
\r
464 private static final int ROOT_MISSING = 0;
\r
465 private static final int ROOT_ICU = 1;
\r
466 private static final int ROOT_JAVA = 2;
\r
468 private static SoftReference<Map<String, Integer>> ROOT_CACHE;
\r
470 private static int getRootType(String baseName, ClassLoader root) {
\r
471 Map<String, Integer> m = null;
\r
474 if (ROOT_CACHE != null) {
\r
475 m = ROOT_CACHE.get();
\r
479 m = new HashMap<String, Integer>();
\r
480 ROOT_CACHE = new SoftReference<Map<String, Integer>>(m);
\r
483 rootType = m.get(baseName);
\r
485 if (rootType == null) {
\r
486 String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
\r
487 int rt = ROOT_MISSING; // value set on success
\r
489 ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true);
\r
491 }catch(MissingResourceException ex){
\r
493 ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true);
\r
495 }catch(MissingResourceException e){
\r
496 //throw away the exception
\r
500 rootType = Integer.valueOf(rt);
\r
501 m.put(baseName, rootType);
\r
504 return rootType.intValue();
\r
507 private static void setRootType(String baseName, int rootType) {
\r
508 Integer rt = Integer.valueOf(rootType);
\r
509 Map<String, Integer> m = null;
\r
511 if (ROOT_CACHE != null) {
\r
512 m = ROOT_CACHE.get();
\r
514 m = new HashMap<String, Integer>();
\r
515 ROOT_CACHE = new SoftReference<Map<String, Integer>>(m);
\r
518 m.put(baseName, rt);
\r
522 * {@icu} Loads a new resource bundle for the given base name, locale and class loader.
\r
523 * Optionally will disable loading of fallback bundles.
\r
524 * @param baseName the base name of the resource bundle, a fully qualified class name
\r
525 * @param localeName the locale for which a resource bundle is desired
\r
526 * @param root the class object from which to load the resource bundle
\r
527 * @param disableFallback disables loading of fallback lookup chain
\r
528 * @throws MissingResourceException If no resource bundle for the specified base name
\r
530 * @return a resource bundle for the given base name and locale
\r
533 protected static UResourceBundle instantiateBundle(String baseName, String localeName,
\r
534 ClassLoader root, boolean disableFallback) {
\r
535 UResourceBundle b = null;
\r
536 int rootType = getRootType(baseName, root);
\r
538 ULocale defaultLocale = ULocale.getDefault();
\r
543 if(disableFallback) {
\r
544 String fullName = ICUResourceBundle.getFullName(baseName, localeName);
\r
545 b = loadFromCache(root, fullName, defaultLocale);
\r
547 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root,
\r
551 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root,
\r
558 return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root,
\r
563 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root,
\r
565 setRootType(baseName, ROOT_ICU);
\r
566 }catch(MissingResourceException ex){
\r
567 b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root,
\r
569 setRootType(baseName, ROOT_JAVA);
\r
576 * {@icu} Returns a binary data item from a binary resource, as a read-only ByteBuffer.
\r
578 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL
\r
580 * @see #getIntVector
\r
582 * @throws MissingResourceException If no resource bundle can be found.
\r
583 * @throws UResourceTypeMismatchException If the resource has a type mismatch.
\r
586 public ByteBuffer getBinary() {
\r
587 throw new UResourceTypeMismatchException("");
\r
591 * Returns a string from a string resource type
\r
594 * @see #getBinary()
\r
595 * @see #getIntVector
\r
597 * @throws MissingResourceException If resource bundle is missing.
\r
598 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
\r
601 public String getString() {
\r
602 throw new UResourceTypeMismatchException("");
\r
606 * Returns a string array from a array resource type
\r
609 * @see #getString()
\r
610 * @see #getIntVector
\r
611 * @throws MissingResourceException If resource bundle is missing.
\r
612 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
\r
615 public String[] getStringArray() {
\r
616 throw new UResourceTypeMismatchException("");
\r
620 * {@icu} Returns a binary data from a binary resource, as a byte array with a copy
\r
621 * of the bytes from the resource bundle.
\r
623 * @param ba The byte array to write the bytes to. A null variable is OK.
\r
624 * @return an array of bytes containing the binary data from the resource.
\r
625 * @see #getIntVector
\r
627 * @throws MissingResourceException If resource bundle is missing.
\r
628 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
\r
631 public byte[] getBinary(byte[] ba) {
\r
632 throw new UResourceTypeMismatchException("");
\r
636 * {@icu} Returns a 32 bit integer array from a resource.
\r
638 * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
\r
639 * @see #getBinary()
\r
641 * @throws MissingResourceException If resource bundle is missing.
\r
642 * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
\r
645 public int[] getIntVector() {
\r
646 throw new UResourceTypeMismatchException("");
\r
650 * {@icu} Returns a signed integer from a resource.
\r
652 * @return an integer value
\r
653 * @see #getIntVector
\r
654 * @see #getBinary()
\r
655 * @throws MissingResourceException If resource bundle is missing.
\r
656 * @throws UResourceTypeMismatchException If resource bundle type mismatch.
\r
659 public int getInt() {
\r
660 throw new UResourceTypeMismatchException("");
\r
664 * {@icu} Returns a unsigned integer from a resource.
\r
665 * This integer is originally 28 bit and the sign gets propagated.
\r
667 * @return an integer value
\r
668 * @see #getIntVector
\r
669 * @see #getBinary()
\r
670 * @throws MissingResourceException If resource bundle is missing.
\r
671 * @throws UResourceTypeMismatchException If resource bundle type mismatch.
\r
674 public int getUInt() {
\r
675 throw new UResourceTypeMismatchException("");
\r
679 * {@icu} Returns a resource in a given resource that has a given key.
\r
681 * @param aKey a key associated with the wanted resource
\r
682 * @return a resource bundle object representing the resource
\r
683 * @throws MissingResourceException If resource bundle is missing.
\r
686 public UResourceBundle get(String aKey) {
\r
687 UResourceBundle obj = findTopLevel(aKey);
\r
689 String fullName = ICUResourceBundle.getFullName(getBaseName(), getLocaleID());
\r
690 throw new MissingResourceException(
\r
691 "Can't find resource for bundle " + fullName + ", key "
\r
692 + aKey, this.getClass().getName(), aKey);
\r
698 * Returns a resource in a given resource that has a given key, or null if the
\r
699 * resource is not found.
\r
701 * @param aKey the key associated with the wanted resource
\r
702 * @return the resource, or null
\r
703 * @see #get(String)
\r
705 * @deprecated This API is ICU internal only.
\r
707 protected UResourceBundle findTopLevel(String aKey) {
\r
708 // NOTE: this only works for top-level resources. For resources at lower
\r
709 // levels, it fails when you fall back to the parent, since you're now
\r
710 // looking at root resources, not at the corresponding nested resource.
\r
711 for (UResourceBundle res = this; res != null; res = res.getParent()) {
\r
712 UResourceBundle obj = res.handleGet(aKey, null, this);
\r
714 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID());
\r
722 * Returns the string in a given resource at the specified index.
\r
724 * @param index an index to the wanted string.
\r
725 * @return a string which lives in the resource.
\r
726 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
\r
727 * @throws UResourceTypeMismatchException If resource bundle type mismatch.
\r
730 public String getString(int index) {
\r
731 ICUResourceBundle temp = (ICUResourceBundle)get(index);
\r
732 if (temp.getType() == STRING) {
\r
733 return temp.getString();
\r
735 throw new UResourceTypeMismatchException("");
\r
739 * {@icu} Returns the resource in a given resource at the specified index.
\r
741 * @param index an index to the wanted resource.
\r
742 * @return the sub resource UResourceBundle object
\r
743 * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
\r
744 * @throws MissingResourceException If the resource bundle is missing.
\r
747 public UResourceBundle get(int index) {
\r
748 UResourceBundle obj = handleGet(index, null, this);
\r
750 obj = (ICUResourceBundle) getParent();
\r
752 obj = obj.get(index);
\r
755 throw new MissingResourceException(
\r
756 "Can't find resource for bundle "
\r
757 + this.getClass().getName() + ", key "
\r
758 + getKey(), this.getClass().getName(), getKey());
\r
760 ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());
\r
765 * Returns a resource in a given resource that has a given index, or null if the
\r
766 * resource is not found.
\r
768 * @param index the index of the resource
\r
769 * @return the resource, or null
\r
772 * @deprecated This API is ICU internal only.
\r
774 protected UResourceBundle findTopLevel(int index) {
\r
775 // NOTE: this _barely_ works for top-level resources. For resources at lower
\r
776 // levels, it fails when you fall back to the parent, since you're now
\r
777 // looking at root resources, not at the corresponding nested resource.
\r
778 // Not only that, but unless the indices correspond 1-to-1, the index will
\r
779 // lose meaning. Essentially this only works if the child resource arrays
\r
780 // are prefixes of their parent arrays.
\r
781 for (UResourceBundle res = this; res != null; res = res.getParent()) {
\r
782 UResourceBundle obj = res.handleGet(index, null, this);
\r
784 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID());
\r
792 * Returns the keys in this bundle as an enumeration
\r
793 * @return an enumeration containing key strings,
\r
794 * which is empty if this is not a bundle or a table resource
\r
797 public Enumeration<String> getKeys() {
\r
798 return Collections.enumeration(keySet());
\r
802 * Returns a Set of all keys contained in this ResourceBundle and its parent bundles.
\r
803 * @return a Set of all keys contained in this ResourceBundle and its parent bundles,
\r
804 * which is empty if this is not a bundle or a table resource
\r
806 * @deprecated This API is ICU internal only.
\r
808 public Set<String> keySet() {
\r
810 if(isTopLevelResource()) {
\r
811 TreeSet<String> newKeySet;
\r
812 if(parent == null) {
\r
813 newKeySet = new TreeSet<String>();
\r
814 } else if(parent instanceof UResourceBundle) {
\r
815 newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet());
\r
817 // TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6
\r
818 // and remove this else branch.
\r
819 newKeySet = new TreeSet<String>();
\r
820 Enumeration<String> parentKeys = parent.getKeys();
\r
821 while(parentKeys.hasMoreElements()) {
\r
822 newKeySet.add(parentKeys.nextElement());
\r
825 newKeySet.addAll(handleKeySet());
\r
826 keys = Collections.unmodifiableSet(newKeySet);
\r
828 return handleKeySet();
\r
834 private Set<String> keys = null;
\r
836 * Returns a Set of the keys contained <i>only</i> in this ResourceBundle.
\r
837 * This does not include further keys from parent bundles.
\r
838 * @return a Set of the keys contained only in this ResourceBundle,
\r
839 * which is empty if this is not a bundle or a table resource
\r
841 * @deprecated This API is ICU internal only.
\r
843 protected Set<String> handleKeySet() {
\r
844 return Collections.emptySet();
\r
848 * {@icu} Returns the size of a resource. Size for scalar types is always 1, and for
\r
849 * vector/table types is the number of child resources.
\r
851 * <br><b>Note:</b> Integer array is treated as a scalar type. There are no APIs to
\r
852 * access individual members of an integer array. It is always returned as a whole.
\r
853 * @return number of resources in a given resource.
\r
856 public int getSize() {
\r
861 * {@icu} Returns the type of a resource.
\r
862 * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
\r
863 * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
\r
864 * {@link #STRING STRING}, {@link #TABLE TABLE}.
\r
866 * @return type of the given resource.
\r
869 public int getType() {
\r
874 * {@icu} Return the version number associated with this UResourceBundle as an
\r
875 * VersionInfo object.
\r
876 * @return VersionInfo object containing the version of the bundle
\r
879 public VersionInfo getVersion() {
\r
884 * {@icu} Returns the iterator which iterates over this
\r
886 * @return UResourceBundleIterator that iterates over the resources in the bundle
\r
889 public UResourceBundleIterator getIterator() {
\r
890 return new UResourceBundleIterator(this);
\r
894 * {@icu} Returns the key associated with a given resource. Not all the resources have
\r
895 * a key - only those that are members of a table.
\r
896 * @return a key associated to this resource, or null if it doesn't have a key
\r
899 public String getKey() {
\r
904 * {@icu} Resource type constant for "no resource".
\r
907 public static final int NONE = -1;
\r
910 * {@icu} Resource type constant for strings.
\r
913 public static final int STRING = 0;
\r
916 * {@icu} Resource type constant for binary data.
\r
919 public static final int BINARY = 1;
\r
922 * {@icu} Resource type constant for tables of key-value pairs.
\r
925 public static final int TABLE = 2;
\r
928 * {@icu} Resource type constant for a single 28-bit integer, interpreted as
\r
929 * signed or unsigned by the getInt() function.
\r
933 public static final int INT = 7;
\r
936 * {@icu} Resource type constant for arrays of resources.
\r
939 public static final int ARRAY = 8;
\r
942 * Resource type constant for vectors of 32-bit integers.
\r
943 * @see #getIntVector
\r
946 public static final int INT_VECTOR = 14;
\r
948 //====== protected members ==============
\r
951 * {@icu} Actual worker method for fetching a resource based on the given key.
\r
952 * Sub classes must override this method if they support resources with keys.
\r
953 * @param aKey the key string of the resource to be fetched
\r
954 * @param table hashtable object to hold references of resources already seen
\r
955 * @param requested the original resource bundle object on which the get method was invoked.
\r
956 * The requested bundle and the bundle on which this method is invoked
\r
957 * are the same, except in the cases where aliases are involved.
\r
958 * @return UResourceBundle a resource associated with the key
\r
961 protected UResourceBundle handleGet(String aKey, HashMap<String, String> table,
\r
962 UResourceBundle requested) {
\r
967 * {@icu} Actual worker method for fetching a resource based on the given index.
\r
968 * Sub classes must override this method if they support arrays of resources.
\r
969 * @param index the index of the resource to be fetched
\r
970 * @param table hashtable object to hold references of resources already seen
\r
971 * @param requested the original resource bundle object on which the get method was invoked.
\r
972 * The requested bundle and the bundle on which this method is invoked
\r
973 * are the same, except in the cases where aliases are involved.
\r
974 * @return UResourceBundle a resource associated with the index
\r
977 protected UResourceBundle handleGet(int index, HashMap<String, String> table,
\r
978 UResourceBundle requested) {
\r
983 * {@icu} Actual worker method for fetching the array of strings in a resource.
\r
984 * Sub classes must override this method if they support arrays of strings.
\r
985 * @return String[] An array of strings containing strings
\r
988 protected String[] handleGetStringArray() {
\r
993 * {@icu} Actual worker method for fetching the keys of resources contained in the resource.
\r
994 * Sub classes must override this method if they support keys and associated resources.
\r
996 * @return Enumeration An enumeration of all the keys in this resource.
\r
999 protected Enumeration<String> handleGetKeys(){
\r
1007 // this method is declared in ResourceBundle class
\r
1008 // so cannot change the signature
\r
1009 // Override this method
\r
1010 protected Object handleGetObject(String aKey) {
\r
1011 return handleGetObjectImpl(aKey, this);
\r
1015 * Override the superclass method
\r
1017 // To facilitate XPath style aliases we need a way to pass the reference
\r
1018 // to requested locale. The only way I could figure out is to implement
\r
1019 // the look up logic here. This has a disadvantage that if the client
\r
1020 // loads an ICUResourceBundle, calls ResourceBundle.getObject method
\r
1021 // with a key that does not exist in the bundle then the lookup is
\r
1022 // done twice before throwing a MissingResourceExpection.
\r
1023 private Object handleGetObjectImpl(String aKey, UResourceBundle requested) {
\r
1024 Object obj = resolveObject(aKey, requested);
\r
1025 if (obj == null) {
\r
1026 UResourceBundle parentBundle = getParent();
\r
1027 if (parentBundle != null) {
\r
1028 obj = parentBundle.handleGetObjectImpl(aKey, requested);
\r
1031 throw new MissingResourceException(
\r
1032 "Can't find resource for bundle "
\r
1033 + this.getClass().getName() + ", key " + aKey,
\r
1034 this.getClass().getName(), aKey);
\r
1039 // Routine for figuring out the type of object to be returned
\r
1040 // string or string array
\r
1041 private Object resolveObject(String aKey, UResourceBundle requested) {
\r
1042 if (getType() == STRING) {
\r
1043 return getString();
\r
1045 UResourceBundle obj = handleGet(aKey, null, requested);
\r
1046 if (obj != null) {
\r
1047 if (obj.getType() == STRING) {
\r
1048 return obj.getString();
\r
1051 if (obj.getType() == ARRAY) {
\r
1052 return obj.handleGetStringArray();
\r
1054 } catch (UResourceTypeMismatchException ex) {
\r
1062 * This method is for setting the loading status of the resource.
\r
1063 * The status is analogous to the warning status in ICU4C.
\r
1065 * @deprecated This API is ICU internal only.
\r
1067 protected abstract void setLoadingStatus(int newStatus);
\r
1070 * Is this a top-level resource, that is, a whole bundle?
\r
1071 * @return true if this is a top-level resource
\r
1073 * @deprecated This API is ICU internal only.
\r
1075 protected boolean isTopLevelResource() {
\r