• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4  *******************************************************************************
5  * Copyright (C) 2004-2016, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  *******************************************************************************
8  */
9 
10 package com.ibm.icu.util;
11 
12 import java.nio.ByteBuffer;
13 import java.util.Collections;
14 import java.util.Enumeration;
15 import java.util.HashMap;
16 import java.util.Locale;
17 import java.util.Map;
18 import java.util.MissingResourceException;
19 import java.util.ResourceBundle;
20 import java.util.Set;
21 import java.util.TreeSet;
22 import java.util.concurrent.ConcurrentHashMap;
23 
24 import com.ibm.icu.impl.ICUData;
25 import com.ibm.icu.impl.ICUResourceBundle;
26 import com.ibm.icu.impl.ICUResourceBundleReader;
27 import com.ibm.icu.impl.ResourceBundleWrapper;
28 
29 /**
30  * {@icuenhanced java.util.ResourceBundle}.{@icu _usage_}
31  *
32  * <p>A class representing a collection of resource information pertaining to a given
33  * locale. A resource bundle provides a way of accessing locale- specific information in a
34  * data file. You create a resource bundle that manages the resources for a given locale
35  * and then ask it for individual resources.
36  *
37  * <p>In ResourceBundle, an object is created and the sub-items are fetched using the
38  * getString and getObject methods.  In UResourceBundle, each individual element of a
39  * resource is a resource by itself.
40  *
41  * <p>Resource bundles in ICU are currently defined using text files that conform to the
42  * following <a
43  * href="http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt">BNF
44  * definition</a>.  More on resource bundle concepts and syntax can be found in the <a
45  * href="http://www.icu-project.org/userguide/ResourceManagement.html">Users Guide</a>.
46  *
47  * <p>The packaging of ICU *.res files can be of two types
48  * ICU4C:
49  * <pre>
50  *       root.res
51  *         |
52  *      --------
53  *     |        |
54  *   fr.res  en.res
55  *     |
56  *   --------
57  *  |        |
58  * fr_CA.res fr_FR.res
59  * </pre>
60  * JAVA/JDK:
61  * <pre>
62  *    LocaleElements.res
63  *         |
64  *      -------------------
65  *     |                   |
66  * LocaleElements_fr.res  LocaleElements_en.res
67  *     |
68  *   ---------------------------
69  *  |                            |
70  * LocaleElements_fr_CA.res   LocaleElements_fr_FR.res
71  * </pre>
72  *
73  * Depending on the organization of your resources, the syntax to getBundleInstance will
74  * change.  To open ICU style organization use:
75  *
76  * <pre>
77  *      UResourceBundle bundle =
78  *          UResourceBundle.getBundleInstance("com/mycompany/resources",
79  *                                            "en_US", myClassLoader);
80  * </pre>
81  * To open Java/JDK style organization use:
82  * <pre>
83  *      UResourceBundle bundle =
84  *          UResourceBundle.getBundleInstance("com.mycompany.resources.LocaleElements",
85  *                                            "en_US", myClassLoader);
86  * </pre>
87  *
88  * <p>Note: Please use pass a class loader for loading non-ICU resources. Java security does not
89  * allow loading of resources across jar files. You must provide your class loader
90  * to load the resources
91 
92  * @stable ICU 3.0
93  * @author ram
94  */
95 public abstract class UResourceBundle extends ResourceBundle {
96 
97 
98     /**
99      * {@icu} Creates a resource bundle using the specified base name and locale.
100      * ICU_DATA_CLASS is used as the default root.
101      * @param baseName string containing the name of the data package.
102      *                    If null the default ICU package name is used.
103      * @param localeName the locale for which a resource bundle is desired
104      * @throws MissingResourceException If no resource bundle for the specified base name
105      * can be found
106      * @return a resource bundle for the given base name and locale
107      * @stable ICU 3.0
108      */
getBundleInstance(String baseName, String localeName)109     public static UResourceBundle getBundleInstance(String baseName, String localeName){
110         return getBundleInstance(baseName, localeName, ICUResourceBundle.ICU_DATA_CLASS_LOADER,
111                                  false);
112     }
113 
114     /**
115      * {@icu} Creates a resource bundle using the specified base name, locale, and class root.
116      *
117      * @param baseName string containing the name of the data package.
118      *                    If null the default ICU package name is used.
119      * @param localeName the locale for which a resource bundle is desired
120      * @param root the class object from which to load the resource bundle
121      * @throws MissingResourceException If no resource bundle for the specified base name
122      * can be found
123      * @return a resource bundle for the given base name and locale
124      * @stable ICU 3.0
125      */
getBundleInstance(String baseName, String localeName, ClassLoader root)126     public static UResourceBundle getBundleInstance(String baseName, String localeName,
127                                                     ClassLoader root){
128         return getBundleInstance(baseName, localeName, root, false);
129     }
130 
131     /**
132      * {@icu} Creates a resource bundle using the specified base name, locale, and class
133      * root.
134      *
135      * @param baseName string containing the name of the data package.
136      *                    If null the default ICU package name is used.
137      * @param localeName the locale for which a resource bundle is desired
138      * @param root the class object from which to load the resource bundle
139      * @param disableFallback Option to disable locale inheritence.
140      *                          If true the fallback chain will not be built.
141      * @throws MissingResourceException
142      *     if no resource bundle for the specified base name can be found
143      * @return a resource bundle for the given base name and locale
144      * @stable ICU 3.0
145      *
146      */
getBundleInstance(String baseName, String localeName, ClassLoader root, boolean disableFallback)147     protected static UResourceBundle getBundleInstance(String baseName, String localeName,
148                                                        ClassLoader root, boolean disableFallback) {
149         return instantiateBundle(baseName, localeName, root, disableFallback);
150     }
151 
152     /**
153      * {@icu} Sole constructor.  (For invocation by subclass constructors, typically
154      * implicit.)  This is public for compatibility with Java, whose compiler
155      * will generate public default constructors for an abstract class.
156      * @stable ICU 3.0
157      */
UResourceBundle()158     public UResourceBundle() {
159     }
160 
161     /**
162      * {@icu} Creates a UResourceBundle for the locale specified, from which users can extract
163      * resources by using their corresponding keys.
164      * @param locale  specifies the locale for which we want to open the resource.
165      *                If null the bundle for default locale is opened.
166      * @return a resource bundle for the given locale
167      * @stable ICU 3.0
168      */
getBundleInstance(ULocale locale)169     public static UResourceBundle getBundleInstance(ULocale locale) {
170         if (locale==null) {
171             locale = ULocale.getDefault();
172         }
173         return getBundleInstance(ICUData.ICU_BASE_NAME, locale.getBaseName(),
174                                  ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
175     }
176 
177     /**
178      * {@icu} Creates a UResourceBundle for the default locale and specified base name,
179      * from which users can extract resources by using their corresponding keys.
180      * @param baseName string containing the name of the data package.
181      *                    If null the default ICU package name is used.
182      * @return a resource bundle for the given base name and default locale
183      * @stable ICU 3.0
184      */
getBundleInstance(String baseName)185     public static UResourceBundle getBundleInstance(String baseName) {
186         if (baseName == null) {
187             baseName = ICUData.ICU_BASE_NAME;
188         }
189         ULocale uloc = ULocale.getDefault();
190         return getBundleInstance(baseName, uloc.getBaseName(), ICUResourceBundle.ICU_DATA_CLASS_LOADER,
191                                  false);
192     }
193 
194     /**
195      * {@icu} Creates a UResourceBundle for the specified locale and specified base name,
196      * from which users can extract resources by using their corresponding keys.
197      * @param baseName string containing the name of the data package.
198      *                    If null the default ICU package name is used.
199      * @param locale  specifies the locale for which we want to open the resource.
200      *                If null the bundle for default locale is opened.
201      * @return a resource bundle for the given base name and locale
202      * @stable ICU 3.0
203      */
204 
getBundleInstance(String baseName, Locale locale)205     public static UResourceBundle getBundleInstance(String baseName, Locale locale) {
206         if (baseName == null) {
207             baseName = ICUData.ICU_BASE_NAME;
208         }
209         ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
210 
211         return getBundleInstance(baseName, uloc.getBaseName(),
212                                  ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
213     }
214 
215     /**
216      * {@icu} Creates a UResourceBundle, from which users can extract resources by using
217      * their corresponding keys.
218      * @param baseName string containing the name of the data package.
219      *                    If null the default ICU package name is used.
220      * @param locale  specifies the locale for which we want to open the resource.
221      *                If null the bundle for default locale is opened.
222      * @return a resource bundle for the given base name and locale
223      * @stable ICU 3.0
224      */
getBundleInstance(String baseName, ULocale locale)225     public static UResourceBundle getBundleInstance(String baseName, ULocale locale) {
226         if (baseName == null) {
227             baseName = ICUData.ICU_BASE_NAME;
228         }
229         if (locale == null) {
230             locale = ULocale.getDefault();
231         }
232         return getBundleInstance(baseName, locale.getBaseName(),
233                                  ICUResourceBundle.ICU_DATA_CLASS_LOADER, false);
234     }
235 
236     /**
237      * {@icu} Creates a UResourceBundle for the specified locale and specified base name,
238      * from which users can extract resources by using their corresponding keys.
239      * @param baseName string containing the name of the data package.
240      *                    If null the default ICU package name is used.
241      * @param locale  specifies the locale for which we want to open the resource.
242      *                If null the bundle for default locale is opened.
243      * @param loader  the loader to use
244      * @return a resource bundle for the given base name and locale
245      * @stable ICU 3.8
246      */
getBundleInstance(String baseName, Locale locale, ClassLoader loader)247     public static UResourceBundle getBundleInstance(String baseName, Locale locale,
248                                                     ClassLoader loader) {
249         if (baseName == null) {
250             baseName = ICUData.ICU_BASE_NAME;
251         }
252         ULocale uloc = locale == null ? ULocale.getDefault() : ULocale.forLocale(locale);
253         return getBundleInstance(baseName, uloc.getBaseName(), loader, false);
254     }
255 
256     /**
257      * {@icu} Creates a UResourceBundle, from which users can extract resources by using
258      * their corresponding keys.<br><br>
259      * Note: Please use this API for loading non-ICU resources. Java security does not
260      * allow loading of resources across jar files. You must provide your class loader
261      * to load the resources
262      * @param baseName string containing the name of the data package.
263      *                    If null the default ICU package name is used.
264      * @param locale  specifies the locale for which we want to open the resource.
265      *                If null the bundle for default locale is opened.
266      * @param loader  the loader to use
267      * @return a resource bundle for the given base name and locale
268      * @stable ICU 3.8
269      */
getBundleInstance(String baseName, ULocale locale, ClassLoader loader)270     public static UResourceBundle getBundleInstance(String baseName, ULocale locale,
271                                                     ClassLoader loader) {
272         if (baseName == null) {
273             baseName = ICUData.ICU_BASE_NAME;
274         }
275         if (locale == null) {
276             locale = ULocale.getDefault();
277         }
278         return getBundleInstance(baseName, locale.getBaseName(), loader, false);
279     }
280 
281     /**
282      * {@icu} Returns the RFC 3066 conformant locale id of this resource bundle.
283      * This method can be used after a call to getBundleInstance() to
284      * determine whether the resource bundle returned really
285      * corresponds to the requested locale or is a fallback.
286      *
287      * @return the locale of this resource bundle
288      * @stable ICU 3.0
289      */
getULocale()290     public abstract ULocale getULocale();
291 
292     /**
293      * {@icu} Returns the localeID
294      * @return The string representation of the localeID
295      * @stable ICU 3.0
296      */
getLocaleID()297     protected abstract String getLocaleID();
298 
299     /**
300      * {@icu} Returns the base name of the resource bundle
301      * @return The string representation of the base name
302      * @stable ICU 3.0
303      */
getBaseName()304     protected abstract String getBaseName();
305 
306     /**
307      * {@icu} Returns the parent bundle
308      * @return The parent bundle
309      * @stable ICU 3.0
310      */
getParent()311     protected abstract UResourceBundle getParent();
312 
313 
314     /**
315      * Returns the locale of this bundle
316      * @return the locale of this resource bundle
317      * @stable ICU 3.0
318      */
319     @Override
getLocale()320     public Locale getLocale(){
321         return getULocale().toLocale();
322     }
323 
324     private enum RootType { MISSING, ICU, JAVA }
325 
326     private static Map<String, RootType> ROOT_CACHE = new ConcurrentHashMap<String, RootType>();
327 
getRootType(String baseName, ClassLoader root)328     private static RootType getRootType(String baseName, ClassLoader root) {
329         RootType rootType = ROOT_CACHE.get(baseName);
330 
331         if (rootType == null) {
332             String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
333             try{
334                 ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true);
335                 rootType = RootType.ICU;
336             }catch(MissingResourceException ex){
337                 try{
338                     ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true);
339                     rootType = RootType.JAVA;
340                 }catch(MissingResourceException e){
341                     //throw away the exception
342                     rootType = RootType.MISSING;
343                 }
344             }
345 
346             ROOT_CACHE.put(baseName, rootType);
347         }
348 
349         return rootType;
350     }
351 
setRootType(String baseName, RootType rootType)352     private static void setRootType(String baseName, RootType rootType) {
353         ROOT_CACHE.put(baseName, rootType);
354     }
355 
356     /**
357      * {@icu} Loads a new resource bundle for the given base name, locale and class loader.
358      * Optionally will disable loading of fallback bundles.
359      * @param baseName string containing the name of the data package.
360      *                    If null the default ICU package name is used.
361      * @param localeName the locale for which a resource bundle is desired
362      * @param root the class object from which to load the resource bundle
363      * @param disableFallback disables loading of fallback lookup chain
364      * @throws MissingResourceException If no resource bundle for the specified base name
365      * can be found
366      * @return a resource bundle for the given base name and locale
367      * @stable ICU 3.0
368      */
instantiateBundle(String baseName, String localeName, ClassLoader root, boolean disableFallback)369     protected static UResourceBundle instantiateBundle(String baseName, String localeName,
370                                                        ClassLoader root, boolean disableFallback) {
371         RootType rootType = getRootType(baseName, root);
372 
373         switch (rootType) {
374         case ICU:
375             return ICUResourceBundle.getBundleInstance(baseName, localeName, root, disableFallback);
376 
377         case JAVA:
378             return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root,
379                                                            disableFallback);
380 
381         case MISSING:
382         default:
383             UResourceBundle b;
384             try{
385                 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root,
386                                                         disableFallback);
387                 setRootType(baseName, RootType.ICU);
388             }catch(MissingResourceException ex){
389                 b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root,
390                                                             disableFallback);
391                 setRootType(baseName, RootType.JAVA);
392             }
393             return b;
394         }
395     }
396 
397     /**
398      * {@icu} Returns a binary data item from a binary resource, as a read-only ByteBuffer.
399      *
400      * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL
401      * file.
402      * @see #getIntVector
403      * @see #getInt
404      * @throws MissingResourceException If no resource bundle can be found.
405      * @throws UResourceTypeMismatchException If the resource has a type mismatch.
406      * @stable ICU 3.8
407      */
getBinary()408     public ByteBuffer getBinary() {
409         throw new UResourceTypeMismatchException("");
410     }
411 
412     /**
413      * Returns a string from a string resource type
414      *
415      * @return a string
416      * @see #getBinary()
417      * @see #getIntVector
418      * @see #getInt
419      * @throws MissingResourceException If resource bundle is missing.
420      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
421      * @stable ICU 3.8
422      */
getString()423     public String getString() {
424         throw new UResourceTypeMismatchException("");
425     }
426 
427     /**
428      * Returns a string array from a array resource type
429      *
430      * @return a string
431      * @see #getString()
432      * @see #getIntVector
433      * @throws MissingResourceException If resource bundle is missing.
434      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
435      * @stable ICU 3.8
436      */
getStringArray()437     public String[] getStringArray() {
438         throw new UResourceTypeMismatchException("");
439     }
440 
441     /**
442      * {@icu} Returns a binary data from a binary resource, as a byte array with a copy
443      * of the bytes from the resource bundle.
444      *
445      * @param ba  The byte array to write the bytes to. A null variable is OK.
446      * @return an array of bytes containing the binary data from the resource.
447      * @see #getIntVector
448      * @see #getInt
449      * @throws MissingResourceException If resource bundle is missing.
450      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
451      * @stable ICU 3.8
452      */
getBinary(byte[] ba)453     public byte[] getBinary(byte[] ba) {
454         throw new UResourceTypeMismatchException("");
455     }
456 
457     /**
458      * {@icu} Returns a 32 bit integer array from a resource.
459      *
460      * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
461      * @see #getBinary()
462      * @see #getInt
463      * @throws MissingResourceException If resource bundle is missing.
464      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
465      * @stable ICU 3.8
466      */
getIntVector()467     public int[] getIntVector() {
468         throw new UResourceTypeMismatchException("");
469     }
470 
471     /**
472      * {@icu} Returns a signed integer from a resource.
473      *
474      * @return an integer value
475      * @see #getIntVector
476      * @see #getBinary()
477      * @throws MissingResourceException If resource bundle is missing.
478      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
479      * @stable ICU 3.8
480      */
getInt()481     public int getInt() {
482         throw new UResourceTypeMismatchException("");
483     }
484 
485     /**
486      * {@icu} Returns a unsigned integer from a resource.
487      * This integer is originally 28 bit and the sign gets propagated.
488      *
489      * @return an integer value
490      * @see #getIntVector
491      * @see #getBinary()
492      * @throws MissingResourceException If resource bundle is missing.
493      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
494      * @stable ICU 3.8
495      */
getUInt()496     public int getUInt() {
497         throw new UResourceTypeMismatchException("");
498     }
499 
500     /**
501      * {@icu} Returns a resource in a given resource that has a given key.
502      *
503      * @param aKey               a key associated with the wanted resource
504      * @return                  a resource bundle object representing the resource
505      * @throws MissingResourceException If resource bundle is missing.
506      * @stable ICU 3.8
507      */
get(String aKey)508     public UResourceBundle get(String aKey) {
509         UResourceBundle obj = findTopLevel(aKey);
510         if (obj == null) {
511             String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID());
512             throw new MissingResourceException(
513                     "Can't find resource for bundle " + fullName + ", key "
514                     + aKey, this.getClass().getName(), aKey);
515         }
516         return obj;
517     }
518 
519     /**
520      * Returns a resource in a given resource that has a given key, or null if the
521      * resource is not found.
522      *
523      * @param aKey the key associated with the wanted resource
524      * @return the resource, or null
525      * @see #get(String)
526      * @internal
527      * @deprecated This API is ICU internal only.
528      */
529     @Deprecated
findTopLevel(String aKey)530     protected UResourceBundle findTopLevel(String aKey) {
531         // NOTE: this only works for top-level resources.  For resources at lower
532         // levels, it fails when you fall back to the parent, since you're now
533         // looking at root resources, not at the corresponding nested resource.
534         for (UResourceBundle res = this; res != null; res = res.getParent()) {
535             UResourceBundle obj = res.handleGet(aKey, null, this);
536             if (obj != null) {
537                 return obj;
538             }
539         }
540         return null;
541     }
542 
543     /**
544      * Returns the string in a given resource at the specified index.
545      *
546      * @param index an index to the wanted string.
547      * @return a string which lives in the resource.
548      * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
549      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
550      * @stable ICU 3.8
551      */
getString(int index)552     public String getString(int index) {
553         ICUResourceBundle temp = (ICUResourceBundle)get(index);
554         if (temp.getType() == STRING) {
555             return temp.getString();
556         }
557         throw new UResourceTypeMismatchException("");
558     }
559 
560     /**
561      * {@icu} Returns the resource in a given resource at the specified index.
562      *
563      * @param index an index to the wanted resource.
564      * @return the sub resource UResourceBundle object
565      * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
566      * @throws MissingResourceException If the resource bundle is missing.
567      * @stable ICU 3.8
568      */
get(int index)569     public UResourceBundle get(int index) {
570         UResourceBundle obj = handleGet(index, null, this);
571         if (obj == null) {
572             obj = getParent();
573             if (obj != null) {
574                 obj = obj.get(index);
575             }
576             if (obj == null)
577                 throw new MissingResourceException(
578                         "Can't find resource for bundle "
579                                 + this.getClass().getName() + ", key "
580                                 + getKey(), this.getClass().getName(), getKey());
581         }
582         return obj;
583     }
584 
585     /**
586      * Returns a resource in a given resource that has a given index, or null if the
587      * resource is not found.
588      *
589      * @param index the index of the resource
590      * @return the resource, or null
591      * @see #get(int)
592      * @internal
593      * @deprecated This API is ICU internal only.
594      */
595     @Deprecated
findTopLevel(int index)596     protected UResourceBundle findTopLevel(int index) {
597         // NOTE: this _barely_ works for top-level resources.  For resources at lower
598         // levels, it fails when you fall back to the parent, since you're now
599         // looking at root resources, not at the corresponding nested resource.
600         // Not only that, but unless the indices correspond 1-to-1, the index will
601         // lose meaning.  Essentially this only works if the child resource arrays
602         // are prefixes of their parent arrays.
603         for (UResourceBundle res = this; res != null; res = res.getParent()) {
604             UResourceBundle obj = res.handleGet(index, null, this);
605             if (obj != null) {
606                 return obj;
607             }
608         }
609         return null;
610     }
611 
612     /**
613      * Returns the keys in this bundle as an enumeration
614      * @return an enumeration containing key strings,
615      *         which is empty if this is not a bundle or a table resource
616      * @stable ICU 3.8
617      */
618     @Override
getKeys()619     public Enumeration<String> getKeys() {
620         return Collections.enumeration(keySet());
621     }
622 
623     /**
624      * Returns a Set of all keys contained in this ResourceBundle and its parent bundles.
625      * @return a Set of all keys contained in this ResourceBundle and its parent bundles,
626      *         which is empty if this is not a bundle or a table resource
627      * @internal
628      * @deprecated This API is ICU internal only.
629      */
630     @Override
631     @Deprecated
keySet()632     public Set<String> keySet() {
633         // TODO: Java 6 ResourceBundle has keySet() which calls handleKeySet()
634         // and caches the results.
635         // When we upgrade to Java 6, we still need to check for isTopLevelResource().
636         // Keep the else branch as is. The if body should just return super.keySet().
637         // Remove then-redundant caching of the keys.
638         Set<String> keys = null;
639         ICUResourceBundle icurb = null;
640         if(isTopLevelResource() && this instanceof ICUResourceBundle) {
641             // We do not cache the top-level keys in this base class so that
642             // not every string/int/binary... resource has to have a keys cache field.
643             icurb = (ICUResourceBundle)this;
644             keys = icurb.getTopLevelKeySet();
645         }
646         if(keys == null) {
647             if(isTopLevelResource()) {
648                 TreeSet<String> newKeySet;
649                 if(parent == null) {
650                     newKeySet = new TreeSet<String>();
651                 } else if(parent instanceof UResourceBundle) {
652                     newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet());
653                 } else {
654                     // TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6
655                     // and remove this else branch.
656                     newKeySet = new TreeSet<String>();
657                     Enumeration<String> parentKeys = parent.getKeys();
658                     while(parentKeys.hasMoreElements()) {
659                         newKeySet.add(parentKeys.nextElement());
660                     }
661                 }
662                 newKeySet.addAll(handleKeySet());
663                 keys = Collections.unmodifiableSet(newKeySet);
664                 if(icurb != null) {
665                     icurb.setTopLevelKeySet(keys);
666                 }
667             } else {
668                 return handleKeySet();
669             }
670         }
671         return keys;
672     }
673 
674     /**
675      * Returns a Set of the keys contained <i>only</i> in this ResourceBundle.
676      * This does not include further keys from parent bundles.
677      * @return a Set of the keys contained only in this ResourceBundle,
678      *         which is empty if this is not a bundle or a table resource
679      * @internal
680      * @deprecated This API is ICU internal only.
681      */
682     @Override
683     @Deprecated
handleKeySet()684     protected Set<String> handleKeySet() {
685         return Collections.emptySet();
686     }
687 
688     /**
689      * {@icu} Returns the size of a resource. Size for scalar types is always 1, and for
690      * vector/table types is the number of child resources.
691      *
692      * <br><b>Note:</b> Integer array is treated as a scalar type. There are no APIs to
693      * access individual members of an integer array. It is always returned as a whole.
694      * @return number of resources in a given resource.
695      * @stable ICU 3.8
696      */
getSize()697     public int getSize() {
698         return 1;
699     }
700 
701     /**
702      * {@icu} Returns the type of a resource.
703      * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
704      * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
705      * {@link #STRING STRING}, {@link #TABLE TABLE}.
706      *
707      * @return type of the given resource.
708      * @stable ICU 3.8
709      */
getType()710     public int getType() {
711         return NONE;
712     }
713 
714     /**
715      * {@icu} Return the version number associated with this UResourceBundle as an
716      * VersionInfo object.
717      * @return VersionInfo object containing the version of the bundle
718      * @stable ICU 3.8
719      */
getVersion()720     public VersionInfo getVersion() {
721         return null;
722     }
723 
724     /**
725      * {@icu} Returns the iterator which iterates over this
726      * resource bundle
727      * @return UResourceBundleIterator that iterates over the resources in the bundle
728      * @stable ICU 3.8
729      */
getIterator()730     public UResourceBundleIterator getIterator() {
731         return new UResourceBundleIterator(this);
732     }
733 
734     /**
735      * {@icu} Returns the key associated with a given resource. Not all the resources have
736      * a key - only those that are members of a table.
737      * @return a key associated to this resource, or null if it doesn't have a key
738      * @stable ICU 3.8
739      */
getKey()740     public String getKey() {
741         return null;
742     }
743 
744     /**
745      * {@icu} Resource type constant for "no resource".
746      * @stable ICU 3.8
747      */
748     public static final int NONE = -1;
749 
750     /**
751      * {@icu} Resource type constant for strings.
752      * @stable ICU 3.8
753      */
754     public static final int STRING = 0;
755 
756     /**
757      * {@icu} Resource type constant for binary data.
758      * @stable ICU 3.8
759      */
760     public static final int BINARY = 1;
761 
762     /**
763      * {@icu} Resource type constant for tables of key-value pairs.
764      * @stable ICU 3.8
765      */
766     public static final int TABLE = 2;
767 
768     /**
769      * {@icu} Resource type constant for a single 28-bit integer, interpreted as
770      * signed or unsigned by the getInt() function.
771      * @see #getInt
772      * @stable ICU 3.8
773      */
774     public static final int INT = 7;
775 
776     /**
777      * {@icu} Resource type constant for arrays of resources.
778      * @stable ICU 3.8
779      */
780     public static final int ARRAY = 8;
781 
782     /**
783      * Resource type constant for vectors of 32-bit integers.
784      * @see #getIntVector
785      * @stable ICU 3.8
786      */
787     public static final int INT_VECTOR = 14;
788 
789     //====== protected members ==============
790 
791     /**
792      * {@icu} Actual worker method for fetching a resource based on the given key.
793      * Sub classes must override this method if they support resources with keys.
794      * @param aKey the key string of the resource to be fetched
795      * @param aliasesVisited hashtable object to hold references of resources already seen
796      * @param requested the original resource bundle object on which the get method was invoked.
797      *                  The requested bundle and the bundle on which this method is invoked
798      *                  are the same, except in the cases where aliases are involved.
799      * @return UResourceBundle a resource associated with the key
800      * @stable ICU 3.8
801      */
handleGet(String aKey, HashMap<String, String> aliasesVisited, UResourceBundle requested)802     protected UResourceBundle handleGet(String aKey, HashMap<String, String> aliasesVisited,
803                                         UResourceBundle requested) {
804         return null;
805     }
806 
807     /**
808      * {@icu} Actual worker method for fetching a resource based on the given index.
809      * Sub classes must override this method if they support arrays of resources.
810      * @param index the index of the resource to be fetched
811      * @param aliasesVisited hashtable object to hold references of resources already seen
812      * @param requested the original resource bundle object on which the get method was invoked.
813      *                  The requested bundle and the bundle on which this method is invoked
814      *                  are the same, except in the cases where aliases are involved.
815      * @return UResourceBundle a resource associated with the index
816      * @stable ICU 3.8
817      */
handleGet(int index, HashMap<String, String> aliasesVisited, UResourceBundle requested)818     protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
819                                         UResourceBundle requested) {
820         return null;
821     }
822 
823     /**
824      * {@icu} Actual worker method for fetching the array of strings in a resource.
825      * Sub classes must override this method if they support arrays of strings.
826      * @return String[] An array of strings containing strings
827      * @stable ICU 3.8
828      */
handleGetStringArray()829     protected String[] handleGetStringArray() {
830         return null;
831     }
832 
833     /**
834      * {@icu} Actual worker method for fetching the keys of resources contained in the resource.
835      * Sub classes must override this method if they support keys and associated resources.
836      *
837      * @return Enumeration An enumeration of all the keys in this resource.
838      * @stable ICU 3.8
839      */
handleGetKeys()840     protected Enumeration<String> handleGetKeys(){
841         return null;
842     }
843 
844     /**
845      * {@inheritDoc}
846      * @stable ICU 3.8
847      */
848     // this method is declared in ResourceBundle class
849     // so cannot change the signature
850     // Override this method
851     @Override
handleGetObject(String aKey)852     protected Object handleGetObject(String aKey) {
853         return handleGetObjectImpl(aKey, this);
854     }
855 
856     /**
857      * Override the superclass method
858      */
859     // To facilitate XPath style aliases we need a way to pass the reference
860     // to requested locale. The only way I could figure out is to implement
861     // the look up logic here. This has a disadvantage that if the client
862     // loads an ICUResourceBundle, calls ResourceBundle.getObject method
863     // with a key that does not exist in the bundle then the lookup is
864     // done twice before throwing a MissingResourceExpection.
handleGetObjectImpl(String aKey, UResourceBundle requested)865     private Object handleGetObjectImpl(String aKey, UResourceBundle requested) {
866         Object obj = resolveObject(aKey, requested);
867         if (obj == null) {
868             UResourceBundle parentBundle = getParent();
869             if (parentBundle != null) {
870                 obj = parentBundle.handleGetObjectImpl(aKey, requested);
871             }
872             if (obj == null)
873                 throw new MissingResourceException(
874                     "Can't find resource for bundle "
875                     + this.getClass().getName() + ", key " + aKey,
876                     this.getClass().getName(), aKey);
877         }
878         return obj;
879     }
880 
881     // Routine for figuring out the type of object to be returned
882     // string or string array
resolveObject(String aKey, UResourceBundle requested)883     private Object resolveObject(String aKey, UResourceBundle requested) {
884         if (getType() == STRING) {
885             return getString();
886         }
887         UResourceBundle obj = handleGet(aKey, null, requested);
888         if (obj != null) {
889             if (obj.getType() == STRING) {
890                 return obj.getString();
891             }
892             try {
893                 if (obj.getType() == ARRAY) {
894                     return obj.handleGetStringArray();
895                 }
896             } catch (UResourceTypeMismatchException ex) {
897                 return obj;
898             }
899         }
900         return obj;
901     }
902 
903     /**
904      * Is this a top-level resource, that is, a whole bundle?
905      * @return true if this is a top-level resource
906      * @internal
907      * @deprecated This API is ICU internal only.
908      */
909     @Deprecated
isTopLevelResource()910     protected boolean isTopLevelResource() {
911         return true;
912     }
913 }
914