1/* 2 ******************************************************************************* 3 * Copyright (C) 2009-2012, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8package com.ibm.icu.text; 9 10import java.util.ArrayList; 11import java.util.Locale; 12import java.util.MissingResourceException; 13 14import com.ibm.icu.impl.ICUCache; 15import com.ibm.icu.impl.ICUResourceBundle; 16import com.ibm.icu.impl.SimpleCache; 17import com.ibm.icu.lang.UCharacter; 18import com.ibm.icu.util.ULocale; 19import com.ibm.icu.util.ULocale.Category; 20import com.ibm.icu.util.UResourceBundle; 21import com.ibm.icu.util.UResourceBundleIterator; 22 23 24/** 25 * <code>NumberingSystem</code> is the base class for all number 26 * systems. This class provides the interface for setting different numbering 27 * system types, whether it be a simple alternate digit system such as 28 * Thai digits or Devanagari digits, or an algorithmic numbering system such 29 * as Hebrew numbering or Chinese numbering. 30 * 31 * @author John Emmons 32 * @stable ICU 4.2 33 */ 34public class NumberingSystem { 35 36 /** 37 * Default constructor. Returns a numbering system that uses the Western decimal 38 * digits 0 through 9. 39 * @stable ICU 4.2 40 */ 41 public NumberingSystem() { 42 radix = 10; 43 algorithmic = false; 44 desc = "0123456789"; 45 name = "latn"; 46 } 47 48 /** 49 * Factory method for creating a numbering system. 50 * @param radix_in The radix for this numbering system. ICU currently 51 * supports only numbering systems whose radix is 10. 52 * @param isAlgorithmic_in Specifies whether the numbering system is algorithmic 53 * (true) or numeric (false). 54 * @param desc_in String used to describe the characteristics of the numbering 55 * system. For numeric systems, this string contains the digits used by the 56 * numbering system, in order, starting from zero. For algorithmic numbering 57 * systems, the string contains the name of the RBNF ruleset in the locale's 58 * NumberingSystemRules section that will be used to format numbers using 59 * this numbering system. 60 * @stable ICU 4.2 61 */ 62 public static NumberingSystem getInstance(int radix_in, boolean isAlgorithmic_in, String desc_in ) { 63 return getInstance(null,radix_in,isAlgorithmic_in,desc_in); 64 } 65 66 /** 67 * Factory method for creating a numbering system. 68 * @param name_in The string representing the name of the numbering system. 69 * @param radix_in The radix for this numbering system. ICU currently 70 * supports only numbering systems whose radix is 10. 71 * @param isAlgorithmic_in Specifies whether the numbering system is algorithmic 72 * (true) or numeric (false). 73 * @param desc_in String used to describe the characteristics of the numbering 74 * system. For numeric systems, this string contains the digits used by the 75 * numbering system, in order, starting from zero. For algorithmic numbering 76 * systems, the string contains the name of the RBNF ruleset in the locale's 77 * NumberingSystemRules section that will be used to format numbers using 78 * this numbering system. 79 * @stable ICU 4.6 80 */ 81 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 * @stable ICU 4.2 103 */ 104 public static NumberingSystem getInstance(Locale inLocale) { 105 return getInstance(ULocale.forLocale(inLocale)); 106 } 107 108 /** 109 * Returns the default numbering system for the specified ULocale. 110 * @stable ICU 4.2 111 */ 112 public static NumberingSystem getInstance(ULocale locale) { 113 114 final String[] OTHER_NS_KEYWORDS = { "native", "traditional", "finance" }; 115 116 NumberingSystem ns; 117 Boolean nsResolved = true; 118 119 // Check for @numbers 120 String numbersKeyword = locale.getKeywordValue("numbers"); 121 if (numbersKeyword != null ) { 122 for ( String keyword : OTHER_NS_KEYWORDS ) { 123 if ( numbersKeyword.equals(keyword)) { 124 nsResolved = false; 125 break; 126 } 127 } 128 } else { 129 numbersKeyword = "default"; 130 nsResolved = false; 131 } 132 133 if (nsResolved) { 134 ns = getInstanceByName(numbersKeyword); 135 if ( ns != null ) { 136 return ns; 137 } else { // if @numbers keyword points to a bogus numbering system name, we return the default for the locale 138 numbersKeyword = "default"; 139 nsResolved = false; 140 } 141 } 142 143 // Attempt to get the numbering system from the cache 144 String baseName = locale.getBaseName(); 145 ns = cachedLocaleData.get(baseName+"@numbers="+numbersKeyword); 146 if (ns != null ) { 147 return ns; 148 } 149 150 // Cache miss, create new instance 151 152 String originalNumbersKeyword = numbersKeyword; 153 String resolvedNumberingSystem = null; 154 while (!nsResolved) { 155 try { 156 ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale); 157 rb = rb.getWithFallback("NumberElements"); 158 resolvedNumberingSystem = rb.getStringWithFallback(numbersKeyword); 159 nsResolved = true; 160 } catch (MissingResourceException ex) { // Fall back behavior as defined in TR35 161 if (numbersKeyword.equals("native") || numbersKeyword.equals("finance")) { 162 numbersKeyword = "default"; 163 } else if (numbersKeyword.equals("traditional")) { 164 numbersKeyword = "native"; 165 } else { 166 nsResolved = true; 167 } 168 } 169 } 170 171 if (resolvedNumberingSystem != null) { 172 ns = getInstanceByName(resolvedNumberingSystem); 173 } 174 175 if ( ns == null ) { 176 ns = new NumberingSystem(); 177 } 178 179 cachedLocaleData.put(baseName+"@numbers="+originalNumbersKeyword, ns); 180 return ns; 181 182 } 183 184 /** 185 * Returns the default numbering system for the default <code>FORMAT</code> locale. 186 * @see Category#FORMAT 187 * @stable ICU 4.2 188 */ 189 public static NumberingSystem getInstance() { 190 return getInstance(ULocale.getDefault(Category.FORMAT)); 191 } 192 193 /** 194 * Returns a numbering system from one of the predefined numbering systems 195 * known to ICU. Numbering system names are based on the numbering systems 196 * defined in CLDR. To get a list of available numbering systems, use the 197 * getAvailableNames method. 198 * @param name The name of the desired numbering system. Numbering system 199 * names often correspond with the name of the script they are associated 200 * with. For example, "thai" for Thai digits, "hebr" for Hebrew numerals. 201 * @stable ICU 4.2 202 */ 203 public static NumberingSystem getInstanceByName(String name) { 204 int radix; 205 boolean isAlgorithmic; 206 String description; 207 208 // Get the numbering system from the cache 209 NumberingSystem ns = cachedStringData.get(name); 210 if (ns != null ) { 211 return ns; 212 } 213 214 try { 215 UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "numberingSystems"); 216 UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems"); 217 UResourceBundle nsTop = nsCurrent.get(name); 218 219 description = nsTop.getString("desc"); 220 UResourceBundle nsRadixBundle = nsTop.get("radix"); 221 UResourceBundle nsAlgBundle = nsTop.get("algorithmic"); 222 radix = nsRadixBundle.getInt(); 223 int algorithmic = nsAlgBundle.getInt(); 224 225 isAlgorithmic = ( algorithmic == 1 ); 226 227 } catch (MissingResourceException ex) { 228 return null; 229 } 230 231 ns = getInstance(name,radix,isAlgorithmic,description); 232 cachedStringData.put(name, ns); 233 return ns; 234 } 235 236 /** 237 * Returns a string array containing a list of the names of numbering systems 238 * currently known to ICU. 239 * @stable ICU 4.2 240 */ 241 public static String [] getAvailableNames() { 242 243 UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "numberingSystems"); 244 UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems"); 245 UResourceBundle temp; 246 247 String nsName; 248 ArrayList<String> output = new ArrayList<String>(); 249 UResourceBundleIterator it = nsCurrent.getIterator(); 250 while (it.hasNext()) { 251 temp = it.next(); 252 nsName = temp.getKey(); 253 output.add(nsName); 254 } 255 return output.toArray(new String[output.size()]); 256 } 257 258 /** 259 * Convenience method to determine if a given digit string is valid for use as a 260 * descriptor of a numeric ( non-algorithmic ) numbering system. In order for 261 * a digit string to be valid, it must meet the following criteria: 262 * 1. Digits must be in Unicode's basic multilingual plane. 263 * @stable ICU 4.2 264 */ 265 public static boolean isValidDigitString(String str) { 266 267 int c; 268 int i = 0; 269 UCharacterIterator it = UCharacterIterator.getInstance(str); 270 271 it.setToStart(); 272 while ( (c = it.nextCodePoint()) != UCharacterIterator.DONE) { 273 if ( UCharacter.isSupplementary(c)) { // Digits outside the BMP are not currently supported 274 return false; 275 } 276 i++; 277 } 278 if ( i != 10 ) { 279 return false; 280 } 281 return true; 282 } 283 284 /** 285 * Returns the radix of the current numbering system. 286 * @stable ICU 4.2 287 */ 288 public int getRadix() { 289 return radix; 290 } 291 292 /** 293 * Returns the description string of the current numbering system. 294 * The description string describes the characteristics of the numbering 295 * system. For numeric systems, this string contains the digits used by the 296 * numbering system, in order, starting from zero. For algorithmic numbering 297 * systems, the string contains the name of the RBNF ruleset in the locale's 298 * NumberingSystemRules section that will be used to format numbers using 299 * this numbering system. 300 * @stable ICU 4.2 301 */ 302 public String getDescription() { 303 return desc; 304 } 305 306 /** 307 * Returns the string representing the name of the numbering system. 308 * @stable ICU 4.6 309 */ 310 public String getName() { 311 return name; 312 } 313 /** 314 * Returns the numbering system's algorithmic status. If true, 315 * the numbering system is algorithmic and uses an RBNF formatter to 316 * format numerals. If false, the numbering system is numeric and 317 * uses a fixed set of digits. 318 * @stable ICU 4.2 319 */ 320 public boolean isAlgorithmic() { 321 return algorithmic; 322 } 323 324 private String desc; 325 private int radix; 326 private boolean algorithmic; 327 private String name; 328 329 /** 330 * Cache to hold the NumberingSystems by Locale. 331 */ 332 private static ICUCache<String, NumberingSystem> cachedLocaleData = new SimpleCache<String, NumberingSystem>(); 333 334 /** 335 * Cache to hold the NumberingSystems by name. 336 */ 337 private static ICUCache<String, NumberingSystem> cachedStringData = new SimpleCache<String, NumberingSystem>(); 338 339} 340