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