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