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