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