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