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