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