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