Home | History | Annotate | Download | only in text
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5  *******************************************************************************
      6  * Copyright (C) 2009-2016, International Business Machines Corporation and
      7  * others. All Rights Reserved.
      8  *******************************************************************************
      9  */
     10 
     11 package android.icu.text;
     12 
     13 import java.util.ArrayList;
     14 import java.util.Locale;
     15 import java.util.MissingResourceException;
     16 
     17 import android.icu.impl.CacheBase;
     18 import android.icu.impl.ICUData;
     19 import android.icu.impl.ICUResourceBundle;
     20 import android.icu.impl.SoftCache;
     21 import android.icu.util.ULocale;
     22 import android.icu.util.ULocale.Category;
     23 import android.icu.util.UResourceBundle;
     24 import android.icu.util.UResourceBundleIterator;
     25 
     26 
     27 /**
     28  * <code>NumberingSystem</code> is the base class for all number
     29  * systems. This class provides the interface for setting different numbering
     30  * system types, whether it be a simple alternate digit system such as
     31  * Thai digits or Devanagari digits, or an algorithmic numbering system such
     32  * as Hebrew numbering or Chinese numbering.
     33  *
     34  * @author       John Emmons
     35  */
     36 public class NumberingSystem {
     37     private static final String[] OTHER_NS_KEYWORDS = { "native", "traditional", "finance" };
     38 
     39     /**
     40      * For convenience, an instance representing the <em>latn</em> numbering system, which
     41      * corresponds to digits in the ASCII range '0' through '9'.
     42      *
     43      * @hide draft / provisional / internal are hidden on Android
     44      */
     45     public static final NumberingSystem LATIN = lookupInstanceByName("latn");
     46 
     47     /**
     48      * Default constructor.  Returns a numbering system that uses the Western decimal
     49      * digits 0 through 9.
     50      */
     51     public NumberingSystem() {
     52         radix = 10;
     53         algorithmic = false;
     54         desc = "0123456789";
     55         name = "latn";
     56     }
     57 
     58     /**
     59      * Factory method for creating a numbering system.
     60      * @param radix_in The radix for this numbering system.  ICU currently
     61      * supports only numbering systems whose radix is 10.
     62      * @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
     63      * (true) or numeric (false).
     64      * @param desc_in String used to describe the characteristics of the numbering
     65      * system.  For numeric systems, this string contains the digits used by the
     66      * numbering system, in order, starting from zero.  For algorithmic numbering
     67      * systems, the string contains the name of the RBNF ruleset in the locale's
     68      * NumberingSystemRules section that will be used to format numbers using
     69      * this numbering system.
     70      */
     71     public static NumberingSystem getInstance(int radix_in, boolean isAlgorithmic_in, String desc_in ) {
     72         return getInstance(null,radix_in,isAlgorithmic_in,desc_in);
     73     }
     74 
     75     /**
     76      * Factory method for creating a numbering system.
     77      * @param name_in The string representing the name of the numbering system.
     78      * @param radix_in The radix for this numbering system.  ICU currently
     79      * supports only numbering systems whose radix is 10.
     80      * @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
     81      * (true) or numeric (false).
     82      * @param desc_in String used to describe the characteristics of the numbering
     83      * system.  For numeric systems, this string contains the digits used by the
     84      * numbering system, in order, starting from zero.  For algorithmic numbering
     85      * systems, the string contains the name of the RBNF ruleset in the locale's
     86      * NumberingSystemRules section that will be used to format numbers using
     87      * this numbering system.
     88      */
     89 
     90     private static NumberingSystem getInstance(String name_in, int radix_in, boolean isAlgorithmic_in, String desc_in ) {
     91         if ( radix_in < 2 ) {
     92             throw new IllegalArgumentException("Invalid radix for numbering system");
     93         }
     94 
     95         if ( !isAlgorithmic_in ) {
     96             if ( desc_in.codePointCount(0, desc_in.length()) != radix_in || !isValidDigitString(desc_in)) {
     97                 throw new IllegalArgumentException("Invalid digit string for numbering system");
     98             }
     99         }
    100         NumberingSystem ns = new NumberingSystem();
    101         ns.radix = radix_in;
    102         ns.algorithmic = isAlgorithmic_in;
    103         ns.desc = desc_in;
    104         ns.name = name_in;
    105         return ns;
    106     }
    107 
    108     /**
    109      * Returns the default numbering system for the specified locale.
    110      */
    111     public static NumberingSystem getInstance(Locale inLocale) {
    112         return getInstance(ULocale.forLocale(inLocale));
    113     }
    114 
    115     /**
    116      * Returns the default numbering system for the specified ULocale.
    117      */
    118     public static NumberingSystem getInstance(ULocale locale) {
    119         // Check for @numbers
    120         boolean nsResolved = true;
    121         String numbersKeyword = locale.getKeywordValue("numbers");
    122         if (numbersKeyword != null ) {
    123             for ( String keyword : OTHER_NS_KEYWORDS ) {
    124                 if ( numbersKeyword.equals(keyword)) {
    125                     nsResolved = false;
    126                     break;
    127                 }
    128             }
    129         } else {
    130             numbersKeyword = "default";
    131             nsResolved = false;
    132         }
    133 
    134         if (nsResolved) {
    135             NumberingSystem ns = getInstanceByName(numbersKeyword);
    136             if (ns != null) {
    137                 return ns;
    138             }
    139             // If the @numbers keyword points to a bogus numbering system name,
    140             // we return the default for the locale.
    141             numbersKeyword = "default";
    142         }
    143 
    144         // Attempt to get the numbering system from the cache
    145         String baseName = locale.getBaseName();
    146         // TODO: Caching by locale+numbersKeyword could yield a large cache.
    147         // Try to load for each locale the mappings from OTHER_NS_KEYWORDS and default
    148         // to real numbering system names; can we get those from supplemental data?
    149         // Then look up those mappings for the locale and resolve the keyword.
    150         String key = baseName+"@numbers="+numbersKeyword;
    151         LocaleLookupData localeLookupData = new LocaleLookupData(locale, numbersKeyword);
    152         return cachedLocaleData.getInstance(key, localeLookupData);
    153     }
    154 
    155     private static class LocaleLookupData {
    156         public final ULocale locale;
    157         public final String numbersKeyword;
    158 
    159         LocaleLookupData(ULocale locale, String numbersKeyword) {
    160             this.locale = locale;
    161             this.numbersKeyword = numbersKeyword;
    162         }
    163     }
    164 
    165     static NumberingSystem lookupInstanceByLocale(LocaleLookupData localeLookupData) {
    166         ULocale locale = localeLookupData.locale;
    167         ICUResourceBundle rb;
    168         try {
    169             rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
    170             rb = rb.getWithFallback("NumberElements");
    171         } catch (MissingResourceException ex) {
    172             return new NumberingSystem();
    173         }
    174 
    175         String numbersKeyword = localeLookupData.numbersKeyword;
    176         String resolvedNumberingSystem = null;
    177         for (;;) {
    178             try {
    179                 resolvedNumberingSystem = rb.getStringWithFallback(numbersKeyword);
    180                 break;
    181             } catch (MissingResourceException ex) { // Fall back behavior as defined in TR35
    182                 if (numbersKeyword.equals("native") || numbersKeyword.equals("finance")) {
    183                     numbersKeyword = "default";
    184                 } else if (numbersKeyword.equals("traditional")) {
    185                     numbersKeyword = "native";
    186                 } else {
    187                     break;
    188                 }
    189             }
    190         }
    191 
    192         NumberingSystem ns = null;
    193         if (resolvedNumberingSystem != null) {
    194             ns = getInstanceByName(resolvedNumberingSystem);
    195         }
    196 
    197         if (ns == null) {
    198             ns = new NumberingSystem();
    199         }
    200         return ns;
    201     }
    202 
    203     /**
    204      * Returns the default numbering system for the default <code>FORMAT</code> locale.
    205      * @see Category#FORMAT
    206      */
    207     public static NumberingSystem getInstance() {
    208         return getInstance(ULocale.getDefault(Category.FORMAT));
    209     }
    210 
    211     /**
    212      * Returns a numbering system from one of the predefined numbering systems
    213      * known to ICU.  Numbering system names are based on the numbering systems
    214      * defined in CLDR.  To get a list of available numbering systems, use the
    215      * getAvailableNames method.
    216      * @param name The name of the desired numbering system.  Numbering system
    217      * names often correspond with the name of the script they are associated
    218      * with.  For example, "thai" for Thai digits, "hebr" for Hebrew numerals.
    219      */
    220     public static NumberingSystem getInstanceByName(String name) {
    221         // Get the numbering system from the cache.
    222         return cachedStringData.getInstance(name, null /* unused */);
    223     }
    224 
    225     private static NumberingSystem lookupInstanceByName(String name) {
    226         int radix;
    227         boolean isAlgorithmic;
    228         String description;
    229         try {
    230             UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "numberingSystems");
    231             UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
    232             UResourceBundle nsTop = nsCurrent.get(name);
    233 
    234             description = nsTop.getString("desc");
    235             UResourceBundle nsRadixBundle = nsTop.get("radix");
    236             UResourceBundle nsAlgBundle = nsTop.get("algorithmic");
    237             radix = nsRadixBundle.getInt();
    238             int algorithmic = nsAlgBundle.getInt();
    239 
    240             isAlgorithmic = ( algorithmic == 1 );
    241 
    242         } catch (MissingResourceException ex) {
    243             return null;
    244         }
    245 
    246         return getInstance(name, radix, isAlgorithmic, description);
    247     }
    248 
    249     /**
    250      * Returns a string array containing a list of the names of numbering systems
    251      * currently known to ICU.
    252      */
    253     public static String [] getAvailableNames() {
    254 
    255             UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "numberingSystems");
    256             UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
    257             UResourceBundle temp;
    258 
    259             String nsName;
    260             ArrayList<String> output = new ArrayList<String>();
    261             UResourceBundleIterator it = nsCurrent.getIterator();
    262             while (it.hasNext()) {
    263                 temp = it.next();
    264                 nsName = temp.getKey();
    265                 output.add(nsName);
    266             }
    267             return output.toArray(new String[output.size()]);
    268     }
    269 
    270     /**
    271      * Convenience method to determine if a given digit string is valid for use as a
    272      * descriptor of a numeric ( non-algorithmic ) numbering system.  In order for
    273      * a digit string to be valid, it must contain exactly ten Unicode code points.
    274      */
    275     public static boolean isValidDigitString(String str) {
    276         int numCodepoints = str.codePointCount(0, str.length());
    277         return (numCodepoints == 10);
    278     }
    279 
    280     /**
    281      * Returns the radix of the current numbering system.
    282      */
    283     public int getRadix() {
    284         return radix;
    285     }
    286 
    287     /**
    288      * Returns the description string of the current numbering system.
    289      * The description string describes the characteristics of the numbering
    290      * system.  For numeric systems, this string contains the digits used by the
    291      * numbering system, in order, starting from zero.  For algorithmic numbering
    292      * systems, the string contains the name of the RBNF ruleset in the locale's
    293      * NumberingSystemRules section that will be used to format numbers using
    294      * this numbering system.
    295      */
    296     public String getDescription() {
    297         return desc;
    298     }
    299 
    300     /**
    301      * Returns the string representing the name of the numbering system.
    302      */
    303     public String getName() {
    304         return name;
    305     }
    306     /**
    307      * Returns the numbering system's algorithmic status.  If true,
    308      * the numbering system is algorithmic and uses an RBNF formatter to
    309      * format numerals.  If false, the numbering system is numeric and
    310      * uses a fixed set of digits.
    311      */
    312     public boolean isAlgorithmic() {
    313         return algorithmic;
    314     }
    315 
    316     private String desc;
    317     private int radix;
    318     private boolean algorithmic;
    319     private String name;
    320 
    321     /**
    322      * Cache to hold the NumberingSystems by Locale.
    323      */
    324     private static CacheBase<String, NumberingSystem, LocaleLookupData> cachedLocaleData =
    325             new SoftCache<String, NumberingSystem, LocaleLookupData>() {
    326         @Override
    327         protected NumberingSystem createInstance(String key, LocaleLookupData localeLookupData) {
    328             return lookupInstanceByLocale(localeLookupData);
    329         }
    330     };
    331 
    332     /**
    333      * Cache to hold the NumberingSystems by name.
    334      */
    335     private static CacheBase<String, NumberingSystem, Void>  cachedStringData =
    336             new SoftCache<String, NumberingSystem, Void>() {
    337         @Override
    338         protected NumberingSystem createInstance(String key, Void unused) {
    339             return lookupInstanceByName(key);
    340         }
    341     };
    342 }
    343