]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/util/UResourceBundle.java
Added flags.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / util / UResourceBundle.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2004-2013, 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  * <note>
87  * 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  * </note>
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      */
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      */
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      */
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      */
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      */
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      */
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
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      */
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      */
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      */
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      */
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      */
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      */
300     protected abstract String getBaseName();
301
302     /**
303      * {@icu} Returns the parent bundle
304      * @return The parent bundle
305      * @stable ICU 3.0
306      */
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      */
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     public static void resetBundleCache() {
328         /*
329          * A HACK!!!!!
330          * Currently if a resourcebundle with fallback turned ON is added to the cache
331          * and then a getBundleInstance() is called for a bundle with fallback turned OFF
332          * it will actually search the cache for any bundle of the same locale
333          * regaurdless of fallback status. This method has been created so that if
334          * The calling method KNOWS that instances of the other fallback state may be in the
335          * cache, the calling method may call this method to clear out the cache.
336          *
337          */
338         //TODO figure a way around this method(see method comment)
339         BUNDLE_CACHE = new SimpleCache<ResourceCacheKey, UResourceBundle>();
340     }
341
342     /**
343      * Method used by subclasses to add a resource bundle object to the managed
344      * cache.  Works like a putIfAbsent(): If the cache already contains a matching
345      * bundle, that one will be retained and returned.
346      * @internal
347      * @deprecated This API is ICU internal only.
348      */
349     protected static UResourceBundle addToCache(ClassLoader cl, String fullName,
350                                                 ULocale defaultLocale, UResourceBundle b) {
351         synchronized(cacheKey){
352             cacheKey.setKeyValues(cl, fullName, defaultLocale);
353             UResourceBundle cachedBundle = BUNDLE_CACHE.get(cacheKey);
354             if (cachedBundle != null) {
355                 return cachedBundle;
356             }
357             BUNDLE_CACHE.put((ResourceCacheKey)cacheKey.clone(), b);
358             return b;
359         }
360     }
361
362     /**
363      * Method used by sub classes to load a resource bundle object from the managed cache
364      * @internal
365      * @deprecated This API is ICU internal only.
366      */
367     protected static UResourceBundle loadFromCache(ClassLoader cl, String fullName, 
368                                                    ULocale defaultLocale){
369         synchronized(cacheKey){
370             cacheKey.setKeyValues(cl, fullName, defaultLocale);
371             return BUNDLE_CACHE.get(cacheKey);
372         }
373     }
374
375     /**
376      * Key used for cached resource bundles.  The key checks
377      * the resource name, the class root, and the default
378      * locale to determine if the resource is a match to the
379      * requested one. The root may be null, but the
380      * searchName and the default locale must have a non-null value.
381      * Note that the default locale may change over time, and
382      * lookup should always be based on the current default
383      * locale (if at all).
384      */
385     private static final class ResourceCacheKey implements Cloneable {
386         private SoftReference<ClassLoader> loaderRef;
387         private String searchName;
388         private ULocale defaultLocale;
389         private int hashCodeCache;
390         ///CLOVER:OFF
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                 //are refs (both non-null) or (both null)?
419                 if (loaderRef == null) {
420                     return otherEntry.loaderRef == null;
421                 } else {
422                     return (otherEntry.loaderRef != null)
423                             && (loaderRef.get() == otherEntry.loaderRef.get());
424                 }
425             } catch (NullPointerException e) {
426                 return false;
427             } catch (ClassCastException e) {
428                 return false;
429             }
430         }
431
432         public int hashCode() {
433             return hashCodeCache;
434         }
435
436         public Object clone() {
437             try {
438                 return super.clone();
439             } catch (CloneNotSupportedException e) {
440                 //this should never happen
441                 throw new IllegalStateException();
442             }
443         }
444
445         ///CLOVER:ON
446         private synchronized void setKeyValues(ClassLoader root, String searchName, 
447                                                ULocale defaultLocale) {
448             this.searchName = searchName;
449             hashCodeCache = searchName.hashCode();
450             this.defaultLocale = defaultLocale;
451             if (defaultLocale != null) {
452                 hashCodeCache ^= defaultLocale.hashCode();
453             }
454             if (root == null) {
455                 this.loaderRef = null;
456             } else {
457                 loaderRef = new SoftReference<ClassLoader>(root);
458                 hashCodeCache ^= root.hashCode();
459             }
460         }
461         /*private void clear() {
462             setKeyValues(null, "", null);
463         }*/
464     }
465
466     private static final ResourceCacheKey cacheKey = new ResourceCacheKey();
467
468     private static final int ROOT_MISSING = 0;
469     private static final int ROOT_ICU = 1;
470     private static final int ROOT_JAVA = 2;
471
472     private static SoftReference<ConcurrentHashMap<String, Integer>> ROOT_CACHE =
473             new SoftReference<ConcurrentHashMap<String, Integer>>(new ConcurrentHashMap<String, Integer>());
474
475     private static int getRootType(String baseName, ClassLoader root) {
476         ConcurrentHashMap<String, Integer> m = null;
477         Integer rootType;
478
479         m = ROOT_CACHE.get();
480         if (m == null) {
481             synchronized(UResourceBundle.class) {
482                 m = ROOT_CACHE.get();
483                 if (m == null) {
484                     m = new ConcurrentHashMap<String, Integer>();
485                     ROOT_CACHE = new SoftReference<ConcurrentHashMap<String, Integer>>(m);
486                 }
487             }
488         }
489
490         rootType = m.get(baseName);
491
492         if (rootType == null) {
493             String rootLocale = (baseName.indexOf('.')==-1) ? "root" : "";
494             int rt = ROOT_MISSING; // value set on success
495             try{
496                 ICUResourceBundle.getBundleInstance(baseName, rootLocale, root, true);
497                 rt = ROOT_ICU;
498             }catch(MissingResourceException ex){
499                 try{
500                     ResourceBundleWrapper.getBundleInstance(baseName, rootLocale, root, true);
501                     rt = ROOT_JAVA;
502                 }catch(MissingResourceException e){
503                     //throw away the exception
504                 }
505             }
506
507             rootType = Integer.valueOf(rt);
508             m.putIfAbsent(baseName, rootType);
509         }
510
511         return rootType.intValue();
512     }
513
514     private static void setRootType(String baseName, int rootType) {
515         Integer rt = Integer.valueOf(rootType);
516         ConcurrentHashMap<String, Integer> m = null;
517
518         m = ROOT_CACHE.get();
519         if (m == null) {
520             synchronized(UResourceBundle.class) {
521                 m = ROOT_CACHE.get();
522                 if (m == null) {
523                     m = new ConcurrentHashMap<String, Integer>();
524                     ROOT_CACHE = new SoftReference<ConcurrentHashMap<String, Integer>>(m);
525                 }
526             }
527         }
528
529         m.put(baseName, rt);
530     }
531
532     /**
533      * {@icu} Loads a new resource bundle for the given base name, locale and class loader.
534      * Optionally will disable loading of fallback bundles.
535      * @param baseName the base name of the resource bundle, a fully qualified class name
536      * @param localeName the locale for which a resource bundle is desired
537      * @param root the class object from which to load the resource bundle
538      * @param disableFallback disables loading of fallback lookup chain
539      * @throws MissingResourceException If no resource bundle for the specified base name
540      * can be found
541      * @return a resource bundle for the given base name and locale
542      * @stable ICU 3.0
543      */
544     protected static UResourceBundle instantiateBundle(String baseName, String localeName,
545                                                        ClassLoader root, boolean disableFallback) {
546         UResourceBundle b = null;
547         int rootType = getRootType(baseName, root);
548
549         ULocale defaultLocale = ULocale.getDefault();
550
551         switch (rootType)
552         {
553         case ROOT_ICU:
554             if(disableFallback) {
555                 String fullName = ICUResourceBundleReader.getFullName(baseName, localeName);
556                 b = loadFromCache(root, fullName, defaultLocale);
557                 if (b == null) {
558                     b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 
559                                                             disableFallback);
560                 }
561             } else {
562                 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 
563                                                         disableFallback);
564             }
565
566             return b;
567
568         case ROOT_JAVA:
569             return ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 
570                                                            disableFallback);
571
572         default:
573             try{
574                 b = ICUResourceBundle.getBundleInstance(baseName, localeName, root, 
575                                                         disableFallback);
576                 setRootType(baseName, ROOT_ICU);
577             }catch(MissingResourceException ex){
578                 b = ResourceBundleWrapper.getBundleInstance(baseName, localeName, root, 
579                                                             disableFallback);
580                 setRootType(baseName, ROOT_JAVA);
581             }
582             return b;
583         }
584     }
585
586     /**
587      * {@icu} Returns a binary data item from a binary resource, as a read-only ByteBuffer.
588      *
589      * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL
590      * file.
591      * @see #getIntVector
592      * @see #getInt
593      * @throws MissingResourceException If no resource bundle can be found.
594      * @throws UResourceTypeMismatchException If the resource has a type mismatch.
595      * @stable ICU 3.8
596      */
597     public ByteBuffer getBinary() {
598         throw new UResourceTypeMismatchException("");
599     }
600
601     /**
602      * Returns a string from a string resource type
603      *
604      * @return a string
605      * @see #getBinary()
606      * @see #getIntVector
607      * @see #getInt
608      * @throws MissingResourceException If resource bundle is missing.
609      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
610      * @stable ICU 3.8
611      */
612     public String getString() {
613         throw new UResourceTypeMismatchException("");
614     }
615
616     /**
617      * Returns a string array from a array resource type
618      *
619      * @return a string
620      * @see #getString()
621      * @see #getIntVector
622      * @throws MissingResourceException If resource bundle is missing.
623      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
624      * @stable ICU 3.8
625      */
626     public String[] getStringArray() {
627         throw new UResourceTypeMismatchException("");
628     }
629
630     /**
631      * {@icu} Returns a binary data from a binary resource, as a byte array with a copy
632      * of the bytes from the resource bundle.
633      *
634      * @param ba  The byte array to write the bytes to. A null variable is OK.
635      * @return an array of bytes containing the binary data from the resource.
636      * @see #getIntVector
637      * @see #getInt
638      * @throws MissingResourceException If resource bundle is missing.
639      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
640      * @stable ICU 3.8
641      */
642     public byte[] getBinary(byte[] ba) {
643         throw new UResourceTypeMismatchException("");
644     }
645
646     /**
647      * {@icu} Returns a 32 bit integer array from a resource.
648      *
649      * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file.
650      * @see #getBinary()
651      * @see #getInt
652      * @throws MissingResourceException If resource bundle is missing.
653      * @throws UResourceTypeMismatchException If resource bundle has a type mismatch.
654      * @stable ICU 3.8
655      */
656     public int[] getIntVector() {
657         throw new UResourceTypeMismatchException("");
658     }
659
660     /**
661      * {@icu} Returns a signed integer from a resource.
662      *
663      * @return an integer value
664      * @see #getIntVector
665      * @see #getBinary()
666      * @throws MissingResourceException If resource bundle is missing.
667      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
668      * @stable ICU 3.8
669      */
670     public int getInt() {
671         throw new UResourceTypeMismatchException("");
672     }
673
674     /**
675      * {@icu} Returns a unsigned integer from a resource.
676      * This integer is originally 28 bit and the sign gets propagated.
677      *
678      * @return an integer value
679      * @see #getIntVector
680      * @see #getBinary()
681      * @throws MissingResourceException If resource bundle is missing.
682      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
683      * @stable ICU 3.8
684      */
685     public int getUInt() {
686         throw new UResourceTypeMismatchException("");
687     }
688
689     /**
690      * {@icu} Returns a resource in a given resource that has a given key.
691      *
692      * @param aKey               a key associated with the wanted resource
693      * @return                  a resource bundle object representing the resource
694      * @throws MissingResourceException If resource bundle is missing.
695      * @stable ICU 3.8
696      */
697     public UResourceBundle get(String aKey) {
698         UResourceBundle obj = findTopLevel(aKey);
699         if (obj == null) {
700             String fullName = ICUResourceBundleReader.getFullName(getBaseName(), getLocaleID());
701             throw new MissingResourceException(
702                     "Can't find resource for bundle " + fullName + ", key "
703                     + aKey, this.getClass().getName(), aKey);
704         }
705         return obj;
706     }
707
708     /**
709      * Returns a resource in a given resource that has a given key, or null if the
710      * resource is not found.
711      *
712      * @param aKey the key associated with the wanted resource
713      * @return the resource, or null
714      * @see #get(String)
715      * @internal
716      * @deprecated This API is ICU internal only.
717      */
718     protected UResourceBundle findTopLevel(String aKey) {
719         // NOTE: this only works for top-level resources.  For resources at lower
720         // levels, it fails when you fall back to the parent, since you're now
721         // looking at root resources, not at the corresponding nested resource.
722         for (UResourceBundle res = this; res != null; res = res.getParent()) {
723             UResourceBundle obj = res.handleGet(aKey, null, this);
724             if (obj != null) {
725                 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID());
726                 return obj;
727             }
728         }
729         return null;
730     }
731
732     /**
733      * Returns the string in a given resource at the specified index.
734      *
735      * @param index an index to the wanted string.
736      * @return a string which lives in the resource.
737      * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
738      * @throws UResourceTypeMismatchException If resource bundle type mismatch.
739      * @stable ICU 3.8
740      */
741     public String getString(int index) {
742         ICUResourceBundle temp = (ICUResourceBundle)get(index);
743         if (temp.getType() == STRING) {
744             return temp.getString();
745         }
746         throw new UResourceTypeMismatchException("");
747     }
748
749     /**
750      * {@icu} Returns the resource in a given resource at the specified index.
751      *
752      * @param index an index to the wanted resource.
753      * @return the sub resource UResourceBundle object
754      * @throws IndexOutOfBoundsException If the index value is out of bounds of accepted values.
755      * @throws MissingResourceException If the resource bundle is missing.
756      * @stable ICU 3.8
757      */
758     public UResourceBundle get(int index) {
759         UResourceBundle obj = handleGet(index, null, this);
760         if (obj == null) {
761             obj = (ICUResourceBundle) getParent();
762             if (obj != null) {
763                 obj = obj.get(index);
764             }
765             if (obj == null)
766                 throw new MissingResourceException(
767                         "Can't find resource for bundle "
768                                 + this.getClass().getName() + ", key "
769                                 + getKey(), this.getClass().getName(), getKey());
770         }
771         ((ICUResourceBundle)obj).setLoadingStatus(getLocaleID());
772         return obj;
773     }
774
775     /**
776      * Returns a resource in a given resource that has a given index, or null if the
777      * resource is not found.
778      *
779      * @param index the index of the resource
780      * @return the resource, or null
781      * @see #get(int)
782      * @internal
783      * @deprecated This API is ICU internal only.
784      */
785     protected UResourceBundle findTopLevel(int index) {
786         // NOTE: this _barely_ works for top-level resources.  For resources at lower
787         // levels, it fails when you fall back to the parent, since you're now
788         // looking at root resources, not at the corresponding nested resource.
789         // Not only that, but unless the indices correspond 1-to-1, the index will
790         // lose meaning.  Essentially this only works if the child resource arrays
791         // are prefixes of their parent arrays.
792         for (UResourceBundle res = this; res != null; res = res.getParent()) {
793             UResourceBundle obj = res.handleGet(index, null, this);
794             if (obj != null) {
795                 ((ICUResourceBundle) obj).setLoadingStatus(getLocaleID());
796                 return obj;
797             }
798         }
799         return null;
800     }
801
802     /**
803      * Returns the keys in this bundle as an enumeration
804      * @return an enumeration containing key strings,
805      *         which is empty if this is not a bundle or a table resource
806      * @stable ICU 3.8
807      */
808     public Enumeration<String> getKeys() {
809         return Collections.enumeration(keySet());
810     }
811
812     /**
813      * Returns a Set of all keys contained in this ResourceBundle and its parent bundles.
814      * @return a Set of all keys contained in this ResourceBundle and its parent bundles,
815      *         which is empty if this is not a bundle or a table resource
816      * @internal
817      * @deprecated This API is ICU internal only.
818      */
819     public Set<String> keySet() {
820         if(keys == null) {
821             if(isTopLevelResource()) {
822                 TreeSet<String> newKeySet;
823                 if(parent == null) {
824                     newKeySet = new TreeSet<String>();
825                 } else if(parent instanceof UResourceBundle) {
826                     newKeySet = new TreeSet<String>(((UResourceBundle)parent).keySet());
827                 } else {
828                     // TODO: Java 6 ResourceBundle has keySet(); use it when we upgrade to Java 6
829                     // and remove this else branch.
830                     newKeySet = new TreeSet<String>();
831                     Enumeration<String> parentKeys = parent.getKeys();
832                     while(parentKeys.hasMoreElements()) {
833                         newKeySet.add(parentKeys.nextElement());
834                     }
835                 }
836                 newKeySet.addAll(handleKeySet());
837                 keys = Collections.unmodifiableSet(newKeySet);
838             } else {
839                 return handleKeySet();
840             }
841         }
842         return keys;
843     }
844
845     private Set<String> keys = null;
846     /**
847      * Returns a Set of the keys contained <i>only</i> in this ResourceBundle.
848      * This does not include further keys from parent bundles.
849      * @return a Set of the keys contained only in this ResourceBundle,
850      *         which is empty if this is not a bundle or a table resource
851      * @internal
852      * @deprecated This API is ICU internal only.
853      */
854     protected Set<String> handleKeySet() {
855         return Collections.emptySet();
856     }
857
858     /**
859      * {@icu} Returns the size of a resource. Size for scalar types is always 1, and for
860      * vector/table types is the number of child resources.
861      * 
862      * <br><b>Note:</b> Integer array is treated as a scalar type. There are no APIs to
863      * access individual members of an integer array. It is always returned as a whole.
864      * @return number of resources in a given resource.
865      * @stable ICU 3.8
866      */
867     public int getSize() {
868         return 1;
869     }
870
871     /**
872      * {@icu} Returns the type of a resource.
873      * Available types are {@link #INT INT}, {@link #ARRAY ARRAY},
874      * {@link #BINARY BINARY}, {@link #INT_VECTOR INT_VECTOR},
875      * {@link #STRING STRING}, {@link #TABLE TABLE}.
876      *
877      * @return type of the given resource.
878      * @stable ICU 3.8
879      */
880     public int getType() {
881         return NONE;
882     }
883
884     /**
885      * {@icu} Return the version number associated with this UResourceBundle as an
886      * VersionInfo object.
887      * @return VersionInfo object containing the version of the bundle
888      * @stable ICU 3.8
889      */
890     public VersionInfo getVersion() {
891         return null;
892     }
893
894     /**
895      * {@icu} Returns the iterator which iterates over this
896      * resource bundle
897      * @return UResourceBundleIterator that iterates over the resources in the bundle
898      * @stable ICU 3.8
899      */
900     public UResourceBundleIterator getIterator() {
901         return new UResourceBundleIterator(this);
902     }
903
904     /**
905      * {@icu} Returns the key associated with a given resource. Not all the resources have
906      * a key - only those that are members of a table.
907      * @return a key associated to this resource, or null if it doesn't have a key
908      * @stable ICU 3.8
909      */
910     public String getKey() {
911         return null;
912     }
913
914     /**
915      * {@icu} Resource type constant for "no resource".
916      * @stable ICU 3.8
917      */
918     public static final int NONE = -1;
919
920     /**
921      * {@icu} Resource type constant for strings.
922      * @stable ICU 3.8
923      */
924     public static final int STRING = 0;
925
926     /**
927      * {@icu} Resource type constant for binary data.
928      * @stable ICU 3.8
929      */
930     public static final int BINARY = 1;
931
932     /**
933      * {@icu} Resource type constant for tables of key-value pairs.
934      * @stable ICU 3.8
935      */
936     public static final int TABLE = 2;
937
938     /**
939      * {@icu} Resource type constant for a single 28-bit integer, interpreted as
940      * signed or unsigned by the getInt() function.
941      * @see #getInt
942      * @stable ICU 3.8
943      */
944     public static final int INT = 7;
945
946     /**
947      * {@icu} Resource type constant for arrays of resources.
948      * @stable ICU 3.8
949      */
950     public static final int ARRAY = 8;
951
952     /**
953      * Resource type constant for vectors of 32-bit integers.
954      * @see #getIntVector
955      * @stable ICU 3.8
956      */
957     public static final int INT_VECTOR = 14;
958
959     //====== protected members ==============
960
961     /**
962      * {@icu} Actual worker method for fetching a resource based on the given key.
963      * Sub classes must override this method if they support resources with keys.
964      * @param aKey the key string of the resource to be fetched
965      * @param table hashtable object to hold references of resources already seen
966      * @param requested the original resource bundle object on which the get method was invoked.
967      *                  The requested bundle and the bundle on which this method is invoked
968      *                  are the same, except in the cases where aliases are involved.
969      * @return UResourceBundle a resource associated with the key
970      * @stable ICU 3.8
971      */
972     protected UResourceBundle handleGet(String aKey, HashMap<String, String> table, 
973                                         UResourceBundle requested) {
974         return null;
975     }
976
977     /**
978      * {@icu} Actual worker method for fetching a resource based on the given index.
979      * Sub classes must override this method if they support arrays of resources.
980      * @param index the index of the resource to be fetched
981      * @param table hashtable object to hold references of resources already seen
982      * @param requested the original resource bundle object on which the get method was invoked.
983      *                  The requested bundle and the bundle on which this method is invoked
984      *                  are the same, except in the cases where aliases are involved.
985      * @return UResourceBundle a resource associated with the index
986      * @stable ICU 3.8
987      */
988     protected UResourceBundle handleGet(int index, HashMap<String, String> table, 
989                                         UResourceBundle requested) {
990         return null;
991     }
992
993     /**
994      * {@icu} Actual worker method for fetching the array of strings in a resource.
995      * Sub classes must override this method if they support arrays of strings.
996      * @return String[] An array of strings containing strings
997      * @stable ICU 3.8
998      */
999     protected String[] handleGetStringArray() {
1000         return null;
1001     }
1002
1003     /**
1004      * {@icu} Actual worker method for fetching the keys of resources contained in the resource.
1005      * Sub classes must override this method if they support keys and associated resources.
1006      *
1007      * @return Enumeration An enumeration of all the keys in this resource.
1008      * @stable ICU 3.8
1009      */
1010     protected Enumeration<String> handleGetKeys(){
1011         return null;
1012     }
1013
1014     /**
1015      * {@inheritDoc}
1016      * @stable ICU 3.8
1017      */
1018     // this method is declared in ResourceBundle class
1019     // so cannot change the signature
1020     // Override this method
1021     protected Object handleGetObject(String aKey) {
1022         return handleGetObjectImpl(aKey, this);
1023     }
1024
1025     /**
1026      * Override the superclass method
1027      */
1028     // To facilitate XPath style aliases we need a way to pass the reference
1029     // to requested locale. The only way I could figure out is to implement
1030     // the look up logic here. This has a disadvantage that if the client
1031     // loads an ICUResourceBundle, calls ResourceBundle.getObject method
1032     // with a key that does not exist in the bundle then the lookup is
1033     // done twice before throwing a MissingResourceExpection.
1034     private Object handleGetObjectImpl(String aKey, UResourceBundle requested) {
1035         Object obj = resolveObject(aKey, requested);
1036         if (obj == null) {
1037             UResourceBundle parentBundle = getParent();
1038             if (parentBundle != null) {
1039                 obj = parentBundle.handleGetObjectImpl(aKey, requested);
1040             }
1041             if (obj == null)
1042                 throw new MissingResourceException(
1043                     "Can't find resource for bundle "
1044                     + this.getClass().getName() + ", key " + aKey,
1045                     this.getClass().getName(), aKey);
1046         }
1047         return obj;
1048     }
1049
1050     // Routine for figuring out the type of object to be returned
1051     // string or string array
1052     private Object resolveObject(String aKey, UResourceBundle requested) {
1053         if (getType() == STRING) {
1054             return getString();
1055         }
1056         UResourceBundle obj = handleGet(aKey, null, requested);
1057         if (obj != null) {
1058             if (obj.getType() == STRING) {
1059                 return obj.getString();
1060             }
1061             try {
1062                 if (obj.getType() == ARRAY) {
1063                     return obj.handleGetStringArray();
1064                 }
1065             } catch (UResourceTypeMismatchException ex) {
1066                 return obj;
1067             }
1068         }
1069         return obj;
1070     }
1071
1072     /**
1073      * This method is for setting the loading status of the resource.
1074      * The status is analogous to the warning status in ICU4C.
1075      * @internal
1076      * @deprecated This API is ICU internal only.
1077      */
1078     protected abstract void setLoadingStatus(int newStatus);
1079
1080     /**
1081      * Is this a top-level resource, that is, a whole bundle?
1082      * @return true if this is a top-level resource
1083      * @internal
1084      * @deprecated This API is ICU internal only.
1085      */
1086     protected boolean isTopLevelResource() {
1087         return true;
1088     }
1089 }