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) 1996-2016, International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10 package ohos.global.icu.text; 11 12 import java.io.IOException; 13 import java.io.ObjectInputStream; 14 import java.io.Serializable; 15 import java.util.Arrays; 16 import java.util.Locale; 17 import java.util.MissingResourceException; 18 19 import ohos.global.icu.impl.CacheBase; 20 import ohos.global.icu.impl.CurrencyData; 21 import ohos.global.icu.impl.CurrencyData.CurrencyDisplayInfo; 22 import ohos.global.icu.impl.CurrencyData.CurrencyFormatInfo; 23 import ohos.global.icu.impl.CurrencyData.CurrencySpacingInfo; 24 import ohos.global.icu.impl.ICUData; 25 import ohos.global.icu.impl.ICUResourceBundle; 26 import ohos.global.icu.impl.SoftCache; 27 import ohos.global.icu.impl.UResource; 28 import ohos.global.icu.util.Currency; 29 import ohos.global.icu.util.ICUCloneNotSupportedException; 30 import ohos.global.icu.util.ULocale; 31 import ohos.global.icu.util.ULocale.Category; 32 import ohos.global.icu.util.UResourceBundle; 33 34 /** 35 * <strong>[icu enhancement]</strong> ICU's replacement for {@link java.text.DecimalFormatSymbols}. Methods, fields, and other functionality specific to ICU are labeled '<strong>[icu]</strong>'. 36 * 37 * This class represents the set of symbols (such as the decimal separator, the grouping 38 * separator, and so on) needed by <code>DecimalFormat</code> to format 39 * numbers. <code>DecimalFormat</code> creates for itself an instance of 40 * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any of 41 * these symbols, you can get the <code>DecimalFormatSymbols</code> object from your 42 * <code>DecimalFormat</code> and modify it. 43 * 44 * @see java.util.Locale 45 * @see DecimalFormat 46 * @author Mark Davis 47 * @author Alan Liu 48 */ 49 public class DecimalFormatSymbols implements Cloneable, Serializable { 50 /** 51 * Creates a DecimalFormatSymbols object for the default <code>FORMAT</code> locale. 52 * @see Category#FORMAT 53 */ DecimalFormatSymbols()54 public DecimalFormatSymbols() { 55 this(ULocale.getDefault(Category.FORMAT)); 56 } 57 58 /** 59 * Creates a DecimalFormatSymbols object for the given locale. 60 * @param locale the locale 61 */ DecimalFormatSymbols(Locale locale)62 public DecimalFormatSymbols(Locale locale) { 63 this(ULocale.forLocale(locale)); 64 } 65 66 /** 67 * <strong>[icu]</strong> Creates a DecimalFormatSymbols object for the given locale. 68 * @param locale the locale 69 */ DecimalFormatSymbols(ULocale locale)70 public DecimalFormatSymbols(ULocale locale) { 71 initialize(locale, null); 72 } 73 DecimalFormatSymbols(Locale locale, NumberingSystem ns)74 private DecimalFormatSymbols(Locale locale, NumberingSystem ns) { 75 this(ULocale.forLocale(locale), ns); 76 } 77 DecimalFormatSymbols(ULocale locale, NumberingSystem ns)78 private DecimalFormatSymbols(ULocale locale, NumberingSystem ns) { 79 initialize(locale, ns); 80 } 81 82 /** 83 * Returns a DecimalFormatSymbols instance for the default locale. 84 * 85 * <p><strong>Note:</strong> Unlike 86 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns 87 * <code>new ohos.global.icu.text.DecimalFormatSymbols()</code>. ICU currently does not 88 * support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java 6. 89 * 90 * @return A DecimalFormatSymbols instance. 91 */ getInstance()92 public static DecimalFormatSymbols getInstance() { 93 return new DecimalFormatSymbols(); 94 } 95 96 /** 97 * Returns a DecimalFormatSymbols instance for the given locale. 98 * 99 * <p><strong>Note:</strong> Unlike 100 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns 101 * <code>new ohos.global.icu.text.DecimalFormatSymbols(locale)</code>. ICU currently does 102 * not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java 103 * 6. 104 * 105 * @param locale the locale. 106 * @return A DecimalFormatSymbols instance. 107 */ getInstance(Locale locale)108 public static DecimalFormatSymbols getInstance(Locale locale) { 109 return new DecimalFormatSymbols(locale); 110 } 111 112 /** 113 * Returns a DecimalFormatSymbols instance for the given locale. 114 * 115 * <p><strong>Note:</strong> Unlike 116 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns 117 * <code>new ohos.global.icu.text.DecimalFormatSymbols(locale)</code>. ICU currently does 118 * not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java 119 * 6. 120 * 121 * @param locale the locale. 122 * @return A DecimalFormatSymbols instance. 123 */ getInstance(ULocale locale)124 public static DecimalFormatSymbols getInstance(ULocale locale) { 125 return new DecimalFormatSymbols(locale); 126 } 127 128 /** 129 * <strong>[icu]</strong> Returns a DecimalFormatSymbols instance for the given locale with digits and symbols 130 * corresponding to the given {@link NumberingSystem}. 131 * 132 * <p>This method behaves equivalently to {@link #getInstance} called with a locale having a 133 * "numbers=xxxx" keyword specifying the numbering system by name. 134 * 135 * <p>In this method, the NumberingSystem argument will be used even if the locale has its own 136 * "numbers=xxxx" keyword. 137 * 138 * @param locale the locale. 139 * @param ns the numbering system. 140 * @return A DecimalFormatSymbols instance. 141 */ forNumberingSystem(Locale locale, NumberingSystem ns)142 public static DecimalFormatSymbols forNumberingSystem(Locale locale, NumberingSystem ns) { 143 return new DecimalFormatSymbols(locale, ns); 144 } 145 146 /** 147 * <strong>[icu]</strong> Returns a DecimalFormatSymbols instance for the given locale with digits and symbols 148 * corresponding to the given {@link NumberingSystem}. 149 * 150 * <p>This method behaves equivalently to {@link #getInstance} called with a locale having a 151 * "numbers=xxxx" keyword specifying the numbering system by name. 152 * 153 * <p>In this method, the NumberingSystem argument will be used even if the locale has its own 154 * "numbers=xxxx" keyword. 155 * 156 * @param locale the locale. 157 * @param ns the numbering system. 158 * @return A DecimalFormatSymbols instance. 159 */ forNumberingSystem(ULocale locale, NumberingSystem ns)160 public static DecimalFormatSymbols forNumberingSystem(ULocale locale, NumberingSystem ns) { 161 return new DecimalFormatSymbols(locale, ns); 162 } 163 164 /** 165 * Returns an array of all locales for which the <code>getInstance</code> methods of 166 * this class can return localized instances. 167 * 168 * <p><strong>Note:</strong> Unlike 169 * <code>java.text.DecimalFormatSymbols#getAvailableLocales</code>, this method simply 170 * returns the array of <code>Locale</code>s available for this class. ICU currently 171 * does not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in 172 * Java 6. 173 * 174 * @return An array of <code>Locale</code>s for which localized 175 * <code>DecimalFormatSymbols</code> instances are available. 176 */ getAvailableLocales()177 public static Locale[] getAvailableLocales() { 178 return ICUResourceBundle.getAvailableLocales(); 179 } 180 181 /** 182 * <strong>[icu]</strong> Returns an array of all locales for which the <code>getInstance</code> 183 * methods of this class can return localized instances. 184 * 185 * <p><strong>Note:</strong> Unlike 186 * <code>java.text.DecimalFormatSymbols#getAvailableLocales</code>, this method simply 187 * returns the array of <code>ULocale</code>s available in this class. ICU currently 188 * does not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in 189 * Java 6. 190 * 191 * @return An array of <code>ULocale</code>s for which localized 192 * <code>DecimalFormatSymbols</code> instances are available. 193 * @hide draft / provisional / internal are hidden on OHOS 194 */ getAvailableULocales()195 public static ULocale[] getAvailableULocales() { 196 return ICUResourceBundle.getAvailableULocales(); 197 } 198 199 200 /** 201 * Returns the character used for zero. Different for Arabic, etc. 202 * @return the character 203 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getDigitStrings()} instead. 204 */ getZeroDigit()205 public char getZeroDigit() { 206 return zeroDigit; 207 } 208 209 /** 210 * Returns the array of characters used as digits, in order from 0 through 9 211 * @return The array 212 * @see #getDigitStrings() 213 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getDigitStrings()} instead. 214 */ getDigits()215 public char[] getDigits() { 216 return digits.clone(); 217 } 218 219 /** 220 * Sets the character used for zero. 221 * <p> 222 * <b>Note:</b> This method propagates digit 1 to 223 * digit 9 by incrementing code point one by one. 224 * 225 * @param zeroDigit the zero character. 226 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #setDigitStrings(String[])} instead. 227 */ setZeroDigit(char zeroDigit)228 public void setZeroDigit(char zeroDigit) { 229 this.zeroDigit = zeroDigit; 230 231 // digitStrings or digits might be referencing a cached copy for 232 // optimization purpose, so creating a copy before making a modification 233 digitStrings = digitStrings.clone(); 234 digits = digits.clone(); 235 236 // Make digitStrings field and digits field in sync 237 digitStrings[0] = String.valueOf(zeroDigit); 238 digits[0] = zeroDigit; 239 240 // Always propagate to digits 1-9 for JDK and ICU4C consistency. 241 for (int i = 1; i < 10; i++) { 242 char d = (char)(zeroDigit + i); 243 digitStrings[i] = String.valueOf(d); 244 digits[i] = d; 245 } 246 247 // Update codePointZero: it is simply zeroDigit. 248 codePointZero = zeroDigit; 249 } 250 251 /** 252 * <strong>[icu]</strong> Returns the array of strings used as digits, in order from 0 through 9 253 * @return The array of ten digit strings 254 * @see #setDigitStrings(String[]) 255 */ getDigitStrings()256 public String[] getDigitStrings() { 257 return digitStrings.clone(); 258 } 259 260 /** 261 * Returns the array of strings used as digits, in order from 0 through 9 262 * Package private method - doesn't create a defensively copy. 263 * 264 * <p><strong>WARNING:</strong> Mutating the returned array will cause undefined behavior. 265 * If you need to change the value of the array, use {@link #getDigitStrings} and {@link 266 * #setDigitStrings} instead. 267 * 268 * @return the array of digit strings 269 * @deprecated This API is ICU internal only. 270 * @hide draft / provisional / internal are hidden on OHOS 271 */ 272 @Deprecated getDigitStringsLocal()273 public String[] getDigitStringsLocal() { 274 return digitStrings; 275 } 276 277 /** 278 * If the digit strings array corresponds to a sequence of increasing code points, this method 279 * returns the code point corresponding to the first entry in the digit strings array. If the 280 * digit strings array is <em>not</em> a sequence of increasing code points, returns -1. 281 * 282 * @deprecated This API is ICU internal only. 283 * @hide draft / provisional / internal are hidden on OHOS 284 */ 285 @Deprecated getCodePointZero()286 public int getCodePointZero() { 287 return codePointZero; 288 } 289 290 /** 291 * <strong>[icu]</strong> Sets the array of strings used as digits, in order from 0 through 9 292 * <p> 293 * <b>Note:</b> 294 * <p> 295 * When the input array of digit strings contains any strings 296 * represented by multiple Java chars, then {@link #getDigits()} will return 297 * the default digits ('0' - '9') and {@link #getZeroDigit()} will return the 298 * default zero digit ('0'). 299 * 300 * @param digitStrings The array of digit strings. The length of the array must be exactly 10. 301 * @throws NullPointerException if the <code>digitStrings</code> is null. 302 * @throws IllegalArgumentException if the length of the array is not 10. 303 * @see #getDigitStrings() 304 */ setDigitStrings(String[] digitStrings)305 public void setDigitStrings(String[] digitStrings) { 306 if (digitStrings == null) { 307 throw new NullPointerException("The input digit string array is null"); 308 } 309 if (digitStrings.length != 10) { 310 throw new IllegalArgumentException("Number of digit strings is not 10"); 311 } 312 313 // Scan input array and create char[] representation if possible 314 // Also update codePointZero if possible 315 String[] tmpDigitStrings = new String[10]; 316 char[] tmpDigits = new char[10]; 317 int tmpCodePointZero = -1; 318 for (int i = 0; i < 10; i++) { 319 String digitStr = digitStrings[i]; 320 if (digitStr == null) { 321 throw new IllegalArgumentException("The input digit string array contains a null element"); 322 } 323 tmpDigitStrings[i] = digitStr; 324 int cp, cc; 325 if (digitStr.length() == 0) { 326 cp = -1; 327 cc = 0; 328 } else { 329 cp = Character.codePointAt(digitStrings[i], 0); 330 cc = Character.charCount(cp); 331 } 332 if (cc == digitStr.length()) { 333 // One code point in this digit. 334 // If it is 1 UTF-16 code unit long, set it in tmpDigits. 335 if (cc == 1 && tmpDigits != null) { 336 tmpDigits[i] = (char) cp; 337 } else { 338 tmpDigits = null; 339 } 340 // Check for validity of tmpCodePointZero. 341 if (i == 0) { 342 tmpCodePointZero = cp; 343 } else if (cp != tmpCodePointZero + i) { 344 tmpCodePointZero = -1; 345 } 346 } else { 347 // More than one code point in this digit. 348 // codePointZero and tmpDigits are going to be invalid. 349 tmpCodePointZero = -1; 350 tmpDigits = null; 351 } 352 } 353 354 this.digitStrings = tmpDigitStrings; 355 this.codePointZero = tmpCodePointZero; 356 357 if (tmpDigits == null) { 358 // fallback to the default digit chars 359 this.zeroDigit = DEF_DIGIT_CHARS_ARRAY[0]; 360 this.digits = DEF_DIGIT_CHARS_ARRAY; 361 } else { 362 this.zeroDigit = tmpDigits[0]; 363 this.digits = tmpDigits; 364 } 365 } 366 367 /** 368 * Returns the character used to represent a significant digit in a pattern. 369 * @return the significant digit pattern character 370 */ getSignificantDigit()371 public char getSignificantDigit() { 372 return sigDigit; 373 } 374 375 /** 376 * Sets the character used to represent a significant digit in a pattern. 377 * @param sigDigit the significant digit pattern character 378 */ setSignificantDigit(char sigDigit)379 public void setSignificantDigit(char sigDigit) { 380 this.sigDigit = sigDigit; 381 } 382 383 /** 384 * Returns the character used for grouping separator. Different for French, etc. 385 * @return the thousands character 386 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getGroupingSeparatorString()} instead. 387 */ getGroupingSeparator()388 public char getGroupingSeparator() { 389 return groupingSeparator; 390 } 391 392 /** 393 * Sets the character used for grouping separator. Different for French, etc. 394 * @param groupingSeparator the thousands character 395 * @see #setGroupingSeparatorString(String) 396 */ setGroupingSeparator(char groupingSeparator)397 public void setGroupingSeparator(char groupingSeparator) { 398 this.groupingSeparator = groupingSeparator; 399 this.groupingSeparatorString = String.valueOf(groupingSeparator); 400 } 401 402 /** 403 * <strong>[icu]</strong> Returns the string used for grouping separator. Different for French, etc. 404 * @return the grouping separator string 405 * @see #setGroupingSeparatorString(String) 406 */ getGroupingSeparatorString()407 public String getGroupingSeparatorString() { 408 return groupingSeparatorString; 409 } 410 411 /** 412 * <strong>[icu]</strong> Sets the string used for grouping separator. 413 * <p> 414 * <b>Note:</b> When the input grouping separator String is represented 415 * by multiple Java chars, then {@link #getGroupingSeparator()} will 416 * return the default grouping separator character (','). 417 * 418 * @param groupingSeparatorString the grouping separator string 419 * @throws NullPointerException if <code>groupingSeparatorString</code> is null. 420 * @see #getGroupingSeparatorString() 421 */ setGroupingSeparatorString(String groupingSeparatorString)422 public void setGroupingSeparatorString(String groupingSeparatorString) { 423 if (groupingSeparatorString == null) { 424 throw new NullPointerException("The input grouping separator is null"); 425 } 426 this.groupingSeparatorString = groupingSeparatorString; 427 if (groupingSeparatorString.length() == 1) { 428 this.groupingSeparator = groupingSeparatorString.charAt(0); 429 } else { 430 // Use the default grouping separator character as fallback 431 this.groupingSeparator = DEF_GROUPING_SEPARATOR; 432 } 433 } 434 435 /** 436 * Returns the character used for decimal sign. Different for French, etc. 437 * @return the decimal character 438 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getDecimalSeparatorString()} instead. 439 */ getDecimalSeparator()440 public char getDecimalSeparator() { 441 return decimalSeparator; 442 } 443 444 /** 445 * Sets the character used for decimal sign. Different for French, etc. 446 * @param decimalSeparator the decimal character 447 */ setDecimalSeparator(char decimalSeparator)448 public void setDecimalSeparator(char decimalSeparator) { 449 this.decimalSeparator = decimalSeparator; 450 this.decimalSeparatorString = String.valueOf(decimalSeparator); 451 } 452 453 /** 454 * <strong>[icu]</strong> Returns the string used for decimal sign. 455 * @return the decimal sign string 456 * @see #setDecimalSeparatorString(String) 457 */ getDecimalSeparatorString()458 public String getDecimalSeparatorString() { 459 return decimalSeparatorString; 460 } 461 462 /** 463 * <strong>[icu]</strong> Sets the string used for decimal sign. 464 * <p> 465 * <b>Note:</b> When the input decimal separator String is represented 466 * by multiple Java chars, then {@link #getDecimalSeparator()} will 467 * return the default decimal separator character ('.'). 468 * 469 * @param decimalSeparatorString the decimal sign string 470 * @throws NullPointerException if <code>decimalSeparatorString</code> is null. 471 * @see #getDecimalSeparatorString() 472 */ setDecimalSeparatorString(String decimalSeparatorString)473 public void setDecimalSeparatorString(String decimalSeparatorString) { 474 if (decimalSeparatorString == null) { 475 throw new NullPointerException("The input decimal separator is null"); 476 } 477 this.decimalSeparatorString = decimalSeparatorString; 478 if (decimalSeparatorString.length() == 1) { 479 this.decimalSeparator = decimalSeparatorString.charAt(0); 480 } else { 481 // Use the default decimal separator character as fallback 482 this.decimalSeparator = DEF_DECIMAL_SEPARATOR; 483 } 484 } 485 486 /** 487 * Returns the character used for mille percent sign. Different for Arabic, etc. 488 * @return the mille percent character 489 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getPerMillString()} instead. 490 */ getPerMill()491 public char getPerMill() { 492 return perMill; 493 } 494 495 /** 496 * Sets the character used for mille percent sign. Different for Arabic, etc. 497 * @param perMill the mille percent character 498 */ setPerMill(char perMill)499 public void setPerMill(char perMill) { 500 this.perMill = perMill; 501 this.perMillString = String.valueOf(perMill); 502 } 503 504 /** 505 * <strong>[icu]</strong> Returns the string used for permille sign. 506 * @return the permille string 507 * @see #setPerMillString(String) 508 */ getPerMillString()509 public String getPerMillString() { 510 return perMillString; 511 } 512 513 /** 514 * <strong>[icu]</strong> Sets the string used for permille sign. 515 * <p> 516 * <b>Note:</b> When the input permille String is represented 517 * by multiple Java chars, then {@link #getPerMill()} will 518 * return the default permille character ('‰'). 519 * 520 * @param perMillString the permille string 521 * @throws NullPointerException if <code>perMillString</code> is null. 522 * @see #getPerMillString() 523 */ setPerMillString(String perMillString)524 public void setPerMillString(String perMillString) { 525 if (perMillString == null) { 526 throw new NullPointerException("The input permille string is null"); 527 } 528 this.perMillString = perMillString; 529 if (perMillString.length() == 1) { 530 this.perMill = perMillString.charAt(0); 531 } else { 532 // Use the default permille character as fallback 533 this.perMill = DEF_PERMILL; 534 } 535 } 536 537 /** 538 * Returns the character used for percent sign. Different for Arabic, etc. 539 * @return the percent character 540 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getPercentString()} instead. 541 */ getPercent()542 public char getPercent() { 543 return percent; 544 } 545 546 /** 547 * Sets the character used for percent sign. Different for Arabic, etc. 548 * @param percent the percent character 549 */ setPercent(char percent)550 public void setPercent(char percent) { 551 this.percent = percent; 552 this.percentString = String.valueOf(percent); 553 } 554 555 /** 556 * <strong>[icu]</strong> Returns the string used for percent sign. 557 * @return the percent string 558 * @see #setPercentString(String) 559 */ getPercentString()560 public String getPercentString() { 561 return percentString; 562 } 563 564 /** 565 * <strong>[icu]</strong> Sets the string used for percent sign. 566 * <p> 567 * <b>Note:</b> When the input grouping separator String is represented 568 * by multiple Java chars, then {@link #getPercent()} will 569 * return the default percent sign character ('%'). 570 * 571 * @param percentString the percent string 572 * @throws NullPointerException if <code>percentString</code> is null. 573 * @see #getPercentString() 574 */ setPercentString(String percentString)575 public void setPercentString(String percentString) { 576 if (percentString == null) { 577 throw new NullPointerException("The input percent sign is null"); 578 } 579 this.percentString = percentString; 580 if (percentString.length() == 1) { 581 this.percent = percentString.charAt(0); 582 } else { 583 // Use default percent character as fallback 584 this.percent = DEF_PERCENT; 585 } 586 } 587 588 /** 589 * Returns the character used for a digit in a pattern. 590 * @return the digit pattern character 591 */ getDigit()592 public char getDigit() { 593 return digit; 594 } 595 596 /** 597 * Sets the character used for a digit in a pattern. 598 * @param digit the digit pattern character 599 */ setDigit(char digit)600 public void setDigit(char digit) { 601 this.digit = digit; 602 } 603 604 /** 605 * Returns the character used to separate positive and negative subpatterns 606 * in a pattern. 607 * @return the pattern separator character 608 */ getPatternSeparator()609 public char getPatternSeparator() { 610 return patternSeparator; 611 } 612 613 /** 614 * Sets the character used to separate positive and negative subpatterns 615 * in a pattern. 616 * @param patternSeparator the pattern separator character 617 */ setPatternSeparator(char patternSeparator)618 public void setPatternSeparator(char patternSeparator) { 619 this.patternSeparator = patternSeparator; 620 } 621 622 /** 623 * Returns the String used to represent infinity. Almost always left 624 * unchanged. 625 * @return the Infinity string 626 */ 627 //Bug 4194173 [Richard/GCL] 628 getInfinity()629 public String getInfinity() { 630 return infinity; 631 } 632 633 /** 634 * Sets the String used to represent infinity. Almost always left 635 * unchanged. 636 * @param infinity the Infinity String 637 */ setInfinity(String infinity)638 public void setInfinity(String infinity) { 639 this.infinity = infinity; 640 } 641 642 /** 643 * Returns the String used to represent NaN. Almost always left 644 * unchanged. 645 * @return the NaN String 646 */ 647 //Bug 4194173 [Richard/GCL] getNaN()648 public String getNaN() { 649 return NaN; 650 } 651 652 /** 653 * Sets the String used to represent NaN. Almost always left 654 * unchanged. 655 * @param NaN the NaN String 656 */ setNaN(String NaN)657 public void setNaN(String NaN) { 658 this.NaN = NaN; 659 } 660 661 /** 662 * Returns the character used to represent minus sign. If no explicit 663 * negative format is specified, one is formed by prefixing 664 * minusSign to the positive format. 665 * @return the minus sign character 666 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getMinusSignString()} instead. 667 */ getMinusSign()668 public char getMinusSign() { 669 return minusSign; 670 } 671 672 /** 673 * Sets the character used to represent minus sign. If no explicit 674 * negative format is specified, one is formed by prefixing 675 * minusSign to the positive format. 676 * @param minusSign the minus sign character 677 */ setMinusSign(char minusSign)678 public void setMinusSign(char minusSign) { 679 this.minusSign = minusSign; 680 this.minusString = String.valueOf(minusSign); 681 } 682 683 /** 684 * <strong>[icu]</strong> Returns the string used to represent minus sign. 685 * @return the minus sign string 686 * @see #setMinusSignString(String) 687 */ getMinusSignString()688 public String getMinusSignString() { 689 return minusString; 690 } 691 692 /** 693 * <strong>[icu]</strong> Sets the string used to represent minus sign. 694 * <p> 695 * <b>Note:</b> When the input minus sign String is represented 696 * by multiple Java chars, then {@link #getMinusSign()} will 697 * return the default minus sign character ('-'). 698 * 699 * @param minusSignString the minus sign string 700 * @throws NullPointerException if <code>minusSignString</code> is null. 701 * @see #getGroupingSeparatorString() 702 */ setMinusSignString(String minusSignString)703 public void setMinusSignString(String minusSignString) { 704 if (minusSignString == null) { 705 throw new NullPointerException("The input minus sign is null"); 706 } 707 this.minusString = minusSignString; 708 if (minusSignString.length() == 1) { 709 this.minusSign = minusSignString.charAt(0); 710 } else { 711 // Use the default minus sign as fallback 712 this.minusSign = DEF_MINUS_SIGN; 713 } 714 } 715 716 /** 717 * <strong>[icu]</strong> Returns the localized plus sign. 718 * @return the plus sign, used in localized patterns and formatted 719 * strings 720 * @see #setPlusSign 721 * @see #setMinusSign 722 * @see #getMinusSign 723 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getPlusSignString()} instead. 724 */ getPlusSign()725 public char getPlusSign() { 726 return plusSign; 727 } 728 729 /** 730 * <strong>[icu]</strong> Sets the localized plus sign. 731 * @param plus the plus sign, used in localized patterns and formatted 732 * strings 733 * @see #getPlusSign 734 * @see #setMinusSign 735 * @see #getMinusSign 736 */ setPlusSign(char plus)737 public void setPlusSign(char plus) { 738 this.plusSign = plus; 739 this.plusString = String.valueOf(plus); 740 } 741 742 /** 743 * <strong>[icu]</strong> Returns the string used to represent plus sign. 744 * @return the plus sign string 745 */ getPlusSignString()746 public String getPlusSignString() { 747 return plusString; 748 } 749 750 /** 751 * <strong>[icu]</strong> Sets the localized plus sign string. 752 * <p> 753 * <b>Note:</b> When the input plus sign String is represented 754 * by multiple Java chars, then {@link #getPlusSign()} will 755 * return the default plus sign character ('+'). 756 * 757 * @param plusSignString the plus sign string, used in localized patterns and formatted 758 * strings 759 * @throws NullPointerException if <code>plusSignString</code> is null. 760 * @see #getPlusSignString() 761 */ setPlusSignString(String plusSignString)762 public void setPlusSignString(String plusSignString) { 763 if (plusSignString == null) { 764 throw new NullPointerException("The input plus sign is null"); 765 } 766 this.plusString = plusSignString; 767 if (plusSignString.length() == 1) { 768 this.plusSign = plusSignString.charAt(0); 769 } else { 770 // Use the default plus sign as fallback 771 this.plusSign = DEF_PLUS_SIGN; 772 } 773 } 774 775 /** 776 * Returns the string denoting the local currency. 777 * @return the local currency String. 778 */ getCurrencySymbol()779 public String getCurrencySymbol() { 780 return currencySymbol; 781 } 782 783 /** 784 * Sets the string denoting the local currency. 785 * @param currency the local currency String. 786 */ setCurrencySymbol(String currency)787 public void setCurrencySymbol(String currency) { 788 currencySymbol = currency; 789 } 790 791 /** 792 * Returns the international string denoting the local currency. 793 * @return the international string denoting the local currency 794 */ getInternationalCurrencySymbol()795 public String getInternationalCurrencySymbol() { 796 return intlCurrencySymbol; 797 } 798 799 /** 800 * Sets the international string denoting the local currency. 801 * @param currency the international string denoting the local currency. 802 */ setInternationalCurrencySymbol(String currency)803 public void setInternationalCurrencySymbol(String currency) { 804 intlCurrencySymbol = currency; 805 } 806 807 /** 808 * Returns the currency symbol, for {@link DecimalFormatSymbols#getCurrency()} API 809 * compatibility only. ICU clients should use the Currency API directly. 810 * @return the currency used, or null 811 */ getCurrency()812 public Currency getCurrency() { 813 return currency; 814 } 815 816 /** 817 * Sets the currency. 818 * 819 * <p><strong>Note:</strong> ICU does not use the DecimalFormatSymbols for the currency 820 * any more. This API is present for API compatibility only. 821 * 822 * <p>This also sets the currency symbol attribute to the currency's symbol 823 * in the DecimalFormatSymbols' locale, and the international currency 824 * symbol attribute to the currency's ISO 4217 currency code. 825 * 826 * @param currency the new currency to be used 827 * @throws NullPointerException if <code>currency</code> is null 828 * @see #setCurrencySymbol 829 * @see #setInternationalCurrencySymbol 830 */ setCurrency(Currency currency)831 public void setCurrency(Currency currency) { 832 if (currency == null) { 833 throw new NullPointerException(); 834 } 835 if (currency.equals(this.currency)) { 836 return; 837 } 838 CurrencyDisplayInfo displayInfo = CurrencyData.provider.getInstance(ulocale, true); 839 setCurrencyOrNull(currency, displayInfo); 840 } 841 setCurrencyOrNull(Currency currency, CurrencyDisplayInfo displayInfo)842 private void setCurrencyOrNull(Currency currency, CurrencyDisplayInfo displayInfo) { 843 this.currency = currency; 844 845 if (currency == null) { 846 intlCurrencySymbol = "XXX"; 847 currencySymbol = "\u00A4"; // 'OX' currency symbol 848 currencyPattern = null; 849 return; 850 } 851 852 intlCurrencySymbol = currency.getCurrencyCode(); 853 currencySymbol = currency.getSymbol(ulocale); 854 855 CurrencyFormatInfo formatInfo = displayInfo.getFormatInfo(currency.getCurrencyCode()); 856 if (formatInfo != null) { 857 setMonetaryDecimalSeparatorString(formatInfo.monetaryDecimalSeparator); 858 setMonetaryGroupingSeparatorString(formatInfo.monetaryGroupingSeparator); 859 currencyPattern = formatInfo.currencyPattern; 860 } 861 } 862 863 /** 864 * Returns the monetary decimal separator. 865 * @return the monetary decimal separator character 866 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getMonetaryDecimalSeparatorString()} instead. 867 */ getMonetaryDecimalSeparator()868 public char getMonetaryDecimalSeparator() { 869 return monetarySeparator; 870 } 871 872 /** 873 * Sets the monetary decimal separator. 874 * @param sep the monetary decimal separator character 875 */ setMonetaryDecimalSeparator(char sep)876 public void setMonetaryDecimalSeparator(char sep) { 877 this.monetarySeparator = sep; 878 this.monetarySeparatorString = String.valueOf(sep); 879 } 880 881 /** 882 * <strong>[icu]</strong> Returns the monetary decimal separator string. 883 * @return the monetary decimal separator string 884 * @see #setMonetaryDecimalSeparatorString(String) 885 */ getMonetaryDecimalSeparatorString()886 public String getMonetaryDecimalSeparatorString() { 887 return monetarySeparatorString; 888 } 889 890 /** 891 * <strong>[icu]</strong> Sets the monetary decimal separator string. 892 * <p> 893 * <b>Note:</b> When the input monetary decimal separator String is represented 894 * by multiple Java chars, then {@link #getMonetaryDecimalSeparatorString()} will 895 * return the default monetary decimal separator character ('.'). 896 * 897 * @param sep the monetary decimal separator string 898 * @throws NullPointerException if <code>sep</code> is null. 899 * @see #getMonetaryDecimalSeparatorString() 900 */ setMonetaryDecimalSeparatorString(String sep)901 public void setMonetaryDecimalSeparatorString(String sep) { 902 if (sep == null) { 903 throw new NullPointerException("The input monetary decimal separator is null"); 904 } 905 this.monetarySeparatorString = sep; 906 if (sep.length() == 1) { 907 this.monetarySeparator = sep.charAt(0); 908 } else { 909 // Use default decimap separator character as fallbacl 910 this.monetarySeparator = DEF_DECIMAL_SEPARATOR; 911 } 912 } 913 914 /** 915 * <strong>[icu]</strong> Returns the monetary grouping separator. 916 * @return the monetary grouping separator character 917 * @apiNote <strong>Discouraged:</strong> ICU 58 use {@link #getMonetaryGroupingSeparatorString()} instead. 918 */ getMonetaryGroupingSeparator()919 public char getMonetaryGroupingSeparator() { 920 return monetaryGroupingSeparator; 921 } 922 923 /** 924 * <strong>[icu]</strong> Sets the monetary grouping separator. 925 * @param sep the monetary grouping separator character 926 */ setMonetaryGroupingSeparator(char sep)927 public void setMonetaryGroupingSeparator(char sep) { 928 this.monetaryGroupingSeparator = sep; 929 this.monetaryGroupingSeparatorString = String.valueOf(sep); 930 } 931 932 /** 933 * <strong>[icu]</strong> Returns the monetary grouping separator. 934 * @return the monetary grouping separator string 935 * @see #setMonetaryGroupingSeparatorString(String) 936 */ getMonetaryGroupingSeparatorString()937 public String getMonetaryGroupingSeparatorString() { 938 return monetaryGroupingSeparatorString; 939 } 940 941 /** 942 * <strong>[icu]</strong> Sets the monetary grouping separator string. 943 * <p> 944 * <b>Note:</b> When the input grouping separator String is represented 945 * by multiple Java chars, then {@link #getMonetaryGroupingSeparator()} will 946 * return the default monetary grouping separator character (','). 947 * 948 * @param sep the monetary grouping separator string 949 * @throws NullPointerException if <code>sep</code> is null. 950 * @see #getMonetaryGroupingSeparatorString() 951 */ setMonetaryGroupingSeparatorString(String sep)952 public void setMonetaryGroupingSeparatorString(String sep) { 953 if (sep == null) { 954 throw new NullPointerException("The input monetary grouping separator is null"); 955 } 956 this.monetaryGroupingSeparatorString = sep; 957 if (sep.length() == 1) { 958 this.monetaryGroupingSeparator = sep.charAt(0); 959 } else { 960 // Use default grouping separator character as fallback 961 this.monetaryGroupingSeparator = DEF_GROUPING_SEPARATOR; 962 } 963 } 964 965 /** 966 * Internal API for NumberFormat 967 * @return String currency pattern string 968 * @deprecated This API is for ICU internal use only 969 */ 970 @Deprecated getCurrencyPattern()971 public String getCurrencyPattern() { 972 return currencyPattern; 973 } 974 975 /** 976 * Returns the multiplication sign 977 */ getExponentMultiplicationSign()978 public String getExponentMultiplicationSign() { 979 return exponentMultiplicationSign; 980 } 981 982 /** 983 * Sets the multiplication sign 984 */ setExponentMultiplicationSign(String exponentMultiplicationSign)985 public void setExponentMultiplicationSign(String exponentMultiplicationSign) { 986 this.exponentMultiplicationSign = exponentMultiplicationSign; 987 } 988 989 /** 990 * <strong>[icu]</strong> Returns the string used to separate the mantissa from the exponent. 991 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. 992 * @return the localized exponent symbol, used in localized patterns 993 * and formatted strings 994 * @see #setExponentSeparator 995 */ getExponentSeparator()996 public String getExponentSeparator() { 997 return exponentSeparator; 998 } 999 1000 /** 1001 * <strong>[icu]</strong> Sets the string used to separate the mantissa from the exponent. 1002 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. 1003 * @param exp the localized exponent symbol, used in localized patterns 1004 * and formatted strings 1005 * @see #getExponentSeparator 1006 */ setExponentSeparator(String exp)1007 public void setExponentSeparator(String exp) { 1008 exponentSeparator = exp; 1009 } 1010 1011 /** 1012 * <strong>[icu]</strong> Returns the character used to pad numbers out to a specified width. This is 1013 * not the pad character itself; rather, it is the special pattern character 1014 * <em>preceding</em> the pad character. In the pattern "*_#,##0", '*' is the pad 1015 * escape, and '_' is the pad character. 1016 * @return the character 1017 * @see #setPadEscape 1018 * @see DecimalFormat#getFormatWidth 1019 * @see DecimalFormat#getPadPosition 1020 * @see DecimalFormat#getPadCharacter 1021 */ getPadEscape()1022 public char getPadEscape() { 1023 return padEscape; 1024 } 1025 1026 /** 1027 * <strong>[icu]</strong> Sets the character used to pad numbers out to a specified width. This is not 1028 * the pad character itself; rather, it is the special pattern character 1029 * <em>preceding</em> the pad character. In the pattern "*_#,##0", '*' is the pad 1030 * escape, and '_' is the pad character. 1031 * @see #getPadEscape 1032 * @see DecimalFormat#setFormatWidth 1033 * @see DecimalFormat#setPadPosition 1034 * @see DecimalFormat#setPadCharacter 1035 */ setPadEscape(char c)1036 public void setPadEscape(char c) { 1037 padEscape = c; 1038 } 1039 1040 /** 1041 * <strong>[icu]</strong> Indicates the currency match pattern used in {@link #getPatternForCurrencySpacing}. 1042 */ 1043 public static final int CURRENCY_SPC_CURRENCY_MATCH = 0; 1044 1045 /** 1046 * <strong>[icu]</strong> Indicates the surrounding match pattern used in {@link 1047 * #getPatternForCurrencySpacing}. 1048 */ 1049 public static final int CURRENCY_SPC_SURROUNDING_MATCH = 1; 1050 1051 /** 1052 * <strong>[icu]</strong> Indicates the insertion value used in {@link #getPatternForCurrencySpacing}. 1053 */ 1054 public static final int CURRENCY_SPC_INSERT = 2; 1055 1056 private String[] currencySpcBeforeSym; 1057 private String[] currencySpcAfterSym; 1058 1059 /** 1060 * <strong>[icu]</strong> Returns the desired currency spacing value. Original values come from ICU's 1061 * CLDR data based on the locale provided during construction, and can be null. These 1062 * values govern what and when text is inserted between a currency code/name/symbol 1063 * and the currency amount when formatting money. 1064 * 1065 * <p>For more information, see <a href="http://www.unicode.org/reports/tr35/#Currencies" 1066 * >UTS#35 section 5.10.2</a>. 1067 * 1068 * @param itemType one of CURRENCY_SPC_CURRENCY_MATCH, CURRENCY_SPC_SURROUNDING_MATCH 1069 * or CURRENCY_SPC_INSERT 1070 * @param beforeCurrency true to get the <code>beforeCurrency</code> values, false 1071 * to get the <code>afterCurrency</code> values. 1072 * @return the value, or null. 1073 * @see #setPatternForCurrencySpacing(int, boolean, String) 1074 */ getPatternForCurrencySpacing(int itemType, boolean beforeCurrency)1075 public String getPatternForCurrencySpacing(int itemType, boolean beforeCurrency) { 1076 if (itemType < CURRENCY_SPC_CURRENCY_MATCH || 1077 itemType > CURRENCY_SPC_INSERT ) { 1078 throw new IllegalArgumentException("unknown currency spacing: " + itemType); 1079 } 1080 if (beforeCurrency) { 1081 return currencySpcBeforeSym[itemType]; 1082 } 1083 return currencySpcAfterSym[itemType]; 1084 } 1085 1086 /** 1087 * <strong>[icu]</strong> Sets the indicated currency spacing pattern or value. See {@link 1088 * #getPatternForCurrencySpacing} for more information. 1089 * 1090 * <p>Values for currency match and surrounding match must be {@link 1091 * ohos.global.icu.text.UnicodeSet} patterns. Values for insert can be any string. 1092 * 1093 * <p><strong>Note:</strong> ICU4J does not currently use this information. 1094 * 1095 * @param itemType one of CURRENCY_SPC_CURRENCY_MATCH, CURRENCY_SPC_SURROUNDING_MATCH 1096 * or CURRENCY_SPC_INSERT 1097 * @param beforeCurrency true if the pattern is for before the currency symbol. 1098 * false if the pattern is for after it. 1099 * @param pattern string to override current setting; can be null. 1100 * @see #getPatternForCurrencySpacing(int, boolean) 1101 */ setPatternForCurrencySpacing(int itemType, boolean beforeCurrency, String pattern)1102 public void setPatternForCurrencySpacing(int itemType, boolean beforeCurrency, String pattern) { 1103 if (itemType < CURRENCY_SPC_CURRENCY_MATCH || 1104 itemType > CURRENCY_SPC_INSERT ) { 1105 throw new IllegalArgumentException("unknown currency spacing: " + itemType); 1106 } 1107 if (beforeCurrency) { 1108 currencySpcBeforeSym = currencySpcBeforeSym.clone(); 1109 currencySpcBeforeSym[itemType] = pattern; 1110 } else { 1111 currencySpcAfterSym = currencySpcAfterSym.clone(); 1112 currencySpcAfterSym[itemType] = pattern; 1113 } 1114 } 1115 1116 /** 1117 * Returns the locale for which this object was constructed. 1118 * @return the locale for which this object was constructed 1119 */ getLocale()1120 public Locale getLocale() { 1121 return requestedLocale; 1122 } 1123 1124 /** 1125 * Returns the locale for which this object was constructed. 1126 * @return the locale for which this object was constructed 1127 */ getULocale()1128 public ULocale getULocale() { 1129 return ulocale; 1130 } 1131 1132 /** 1133 * {@inheritDoc} 1134 */ 1135 @Override clone()1136 public Object clone() { 1137 try { 1138 return super.clone(); 1139 // other fields are bit-copied 1140 } catch (CloneNotSupportedException e) { 1141 ///CLOVER:OFF 1142 throw new ICUCloneNotSupportedException(e); 1143 ///CLOVER:ON 1144 } 1145 } 1146 1147 /** 1148 * {@inheritDoc} 1149 */ 1150 @Override equals(Object obj)1151 public boolean equals(Object obj) { 1152 if (!(obj instanceof DecimalFormatSymbols)) { 1153 return false; 1154 } 1155 if (this == obj) { 1156 return true; 1157 } 1158 DecimalFormatSymbols other = (DecimalFormatSymbols) obj; 1159 for (int i = 0; i <= CURRENCY_SPC_INSERT; i++) { 1160 if (!currencySpcBeforeSym[i].equals(other.currencySpcBeforeSym[i])) { 1161 return false; 1162 } 1163 if (!currencySpcAfterSym[i].equals(other.currencySpcAfterSym[i])) { 1164 return false; 1165 } 1166 } 1167 1168 if ( other.digits == null ) { 1169 for (int i = 0 ; i < 10 ; i++) { 1170 if (digits[i] != other.zeroDigit + i) { 1171 return false; 1172 } 1173 } 1174 } else if (!Arrays.equals(digits,other.digits)) { 1175 return false; 1176 } 1177 1178 return ( 1179 groupingSeparator == other.groupingSeparator && 1180 decimalSeparator == other.decimalSeparator && 1181 percent == other.percent && 1182 perMill == other.perMill && 1183 digit == other.digit && 1184 minusSign == other.minusSign && 1185 minusString.equals(other.minusString) && 1186 patternSeparator == other.patternSeparator && 1187 infinity.equals(other.infinity) && 1188 NaN.equals(other.NaN) && 1189 currencySymbol.equals(other.currencySymbol) && 1190 intlCurrencySymbol.equals(other.intlCurrencySymbol) && 1191 padEscape == other.padEscape && 1192 plusSign == other.plusSign && 1193 plusString.equals(other.plusString) && 1194 exponentSeparator.equals(other.exponentSeparator) && 1195 monetarySeparator == other.monetarySeparator && 1196 monetaryGroupingSeparator == other.monetaryGroupingSeparator && 1197 exponentMultiplicationSign.equals(other.exponentMultiplicationSign)); 1198 } 1199 1200 /** 1201 * {@inheritDoc} 1202 */ 1203 @Override hashCode()1204 public int hashCode() { 1205 int result = digits[0]; 1206 result = result * 37 + groupingSeparator; 1207 result = result * 37 + decimalSeparator; 1208 return result; 1209 } 1210 1211 /** 1212 * List of field names to be loaded from the data files. 1213 * The indices of each name into the array correspond to the position of that item in the 1214 * numberElements array. 1215 */ 1216 private static final String[] SYMBOL_KEYS = { 1217 "decimal", 1218 "group", 1219 "percentSign", 1220 "minusSign", 1221 "plusSign", 1222 "exponential", 1223 "perMille", 1224 "infinity", 1225 "nan", 1226 "currencyDecimal", 1227 "currencyGroup", 1228 "superscriptingExponent" 1229 }; 1230 1231 /* 1232 * Default digits 1233 */ 1234 private static final String[] DEF_DIGIT_STRINGS_ARRAY = 1235 {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; 1236 1237 private static final char[] DEF_DIGIT_CHARS_ARRAY = 1238 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; 1239 1240 /* 1241 * Default symbol characters, used for fallbacks. 1242 */ 1243 private static final char DEF_DECIMAL_SEPARATOR = '.'; 1244 private static final char DEF_GROUPING_SEPARATOR = ','; 1245 private static final char DEF_PERCENT = '%'; 1246 private static final char DEF_MINUS_SIGN = '-'; 1247 private static final char DEF_PLUS_SIGN = '+'; 1248 private static final char DEF_PERMILL = '\u2030'; 1249 1250 /** 1251 * List of default values for the symbols. 1252 */ 1253 private static final String[] SYMBOL_DEFAULTS = new String[] { 1254 String.valueOf(DEF_DECIMAL_SEPARATOR), // decimal 1255 String.valueOf(DEF_GROUPING_SEPARATOR), // group 1256 String.valueOf(DEF_PERCENT), // percentSign 1257 String.valueOf(DEF_MINUS_SIGN), // minusSign 1258 String.valueOf(DEF_PLUS_SIGN), // plusSign 1259 "E", // exponential 1260 String.valueOf(DEF_PERMILL), // perMille 1261 "\u221e", // infinity 1262 "NaN", // NaN 1263 null, // currency decimal 1264 null, // currency group 1265 "\u00D7" // superscripting exponent 1266 }; 1267 1268 /** 1269 * Constants for path names in the data bundles. 1270 */ 1271 private static final String LATIN_NUMBERING_SYSTEM = "latn"; 1272 private static final String NUMBER_ELEMENTS = "NumberElements"; 1273 private static final String SYMBOLS = "symbols"; 1274 1275 /** 1276 * Sink for enumerating all of the decimal format symbols (more specifically, anything 1277 * under the "NumberElements.symbols" tree). 1278 * 1279 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): 1280 * Only store a value if it is still missing, that is, it has not been overridden. 1281 */ 1282 private static final class DecFmtDataSink extends UResource.Sink { 1283 1284 private String[] numberElements; // Array where to store the characters (set in constructor) 1285 DecFmtDataSink(String[] numberElements)1286 public DecFmtDataSink(String[] numberElements) { 1287 this.numberElements = numberElements; 1288 } 1289 1290 @Override put(UResource.Key key, UResource.Value value, boolean noFallback)1291 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 1292 UResource.Table symbolsTable = value.getTable(); 1293 for (int j = 0; symbolsTable.getKeyAndValue(j, key, value); ++j) { 1294 for (int i = 0; i < SYMBOL_KEYS.length; i++) { 1295 if (key.contentEquals(SYMBOL_KEYS[i])) { 1296 if (numberElements[i] == null) { 1297 numberElements[i] = value.toString(); 1298 } 1299 break; 1300 } 1301 } 1302 } 1303 } 1304 } 1305 1306 /** 1307 * Initializes the symbols from the locale data. 1308 */ initialize(ULocale locale, NumberingSystem ns)1309 private void initialize(ULocale locale, NumberingSystem ns) { 1310 this.requestedLocale = locale.toLocale(); 1311 this.ulocale = locale; 1312 1313 // TODO: The cache requires a single key, so we just save the NumberingSystem into the 1314 // locale string. NumberingSystem is then decoded again in the loadData() method. It would 1315 // be more efficient if we didn't have to serialize and deserialize the NumberingSystem. 1316 ULocale keyLocale = (ns == null) ? locale : locale.setKeywordValue("numbers", ns.getName()); 1317 CacheData data = cachedLocaleData.getInstance(keyLocale, null /* unused */); 1318 1319 setLocale(data.validLocale, data.validLocale); 1320 setDigitStrings(data.digits); 1321 String[] numberElements = data.numberElements; 1322 1323 // Copy data from the numberElements map into instance fields 1324 setDecimalSeparatorString(numberElements[0]); 1325 setGroupingSeparatorString(numberElements[1]); 1326 // #11897: pattern separator is always ';', not from data 1327 patternSeparator = ';'; 1328 setPercentString(numberElements[2]); 1329 setMinusSignString(numberElements[3]); 1330 setPlusSignString(numberElements[4]); 1331 setExponentSeparator(numberElements[5]); 1332 setPerMillString(numberElements[6]); 1333 setInfinity(numberElements[7]); 1334 setNaN(numberElements[8]); 1335 setMonetaryDecimalSeparatorString(numberElements[9]); 1336 setMonetaryGroupingSeparatorString(numberElements[10]); 1337 setExponentMultiplicationSign(numberElements[11]); 1338 1339 digit = '#'; // Localized pattern character no longer in CLDR 1340 padEscape = '*'; 1341 sigDigit = '@'; 1342 1343 CurrencyDisplayInfo displayInfo = CurrencyData.provider.getInstance(ulocale, true); 1344 initSpacingInfo(displayInfo.getSpacingInfo()); 1345 1346 setCurrencyOrNull(Currency.getInstance(ulocale), displayInfo); 1347 } 1348 loadData(ULocale locale)1349 private static CacheData loadData(ULocale locale) { 1350 String nsName; 1351 // Attempt to set the decimal digits based on the numbering system for the requested locale. 1352 NumberingSystem ns = NumberingSystem.getInstance(locale); 1353 String[] digits = new String[10]; 1354 if (ns != null && ns.getRadix() == 10 && !ns.isAlgorithmic() && 1355 NumberingSystem.isValidDigitString(ns.getDescription())) { 1356 String digitString = ns.getDescription(); 1357 1358 for (int i = 0, offset = 0; i < 10; i++) { 1359 int cp = digitString.codePointAt(offset); 1360 int nextOffset = offset + Character.charCount(cp); 1361 digits[i] = digitString.substring(offset, nextOffset); 1362 offset = nextOffset; 1363 } 1364 nsName = ns.getName(); 1365 } else { 1366 // Default numbering system 1367 digits = DEF_DIGIT_STRINGS_ARRAY; 1368 nsName = "latn"; 1369 } 1370 1371 // Open the resource bundle and get the locale IDs. 1372 // TODO: Is there a better way to get the locale than making an ICUResourceBundle instance? 1373 ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle. 1374 getBundleInstance(ICUData.ICU_BASE_NAME, locale); 1375 // TODO: Determine actual and valid locale correctly. 1376 ULocale validLocale = rb.getULocale(); 1377 1378 String[] numberElements = new String[SYMBOL_KEYS.length]; 1379 1380 // Load using a data sink 1381 DecFmtDataSink sink = new DecFmtDataSink(numberElements); 1382 try { 1383 rb.getAllItemsWithFallback(NUMBER_ELEMENTS + "/" + nsName + "/" + SYMBOLS, sink); 1384 } catch (MissingResourceException e) { 1385 // The symbols don't exist for the given nsName and resource bundle. 1386 // Silently ignore and fall back to Latin. 1387 } 1388 1389 // Load the Latin fallback if necessary 1390 boolean hasNull = false; 1391 for (String entry : numberElements) { 1392 if (entry == null) { 1393 hasNull = true; 1394 break; 1395 } 1396 } 1397 if (hasNull && !nsName.equals(LATIN_NUMBERING_SYSTEM)) { 1398 rb.getAllItemsWithFallback(NUMBER_ELEMENTS + "/" + LATIN_NUMBERING_SYSTEM + "/" + SYMBOLS, sink); 1399 } 1400 1401 // Fill in any remaining missing values 1402 for (int i = 0; i < SYMBOL_KEYS.length; i++) { 1403 if (numberElements[i] == null) { 1404 numberElements[i] = SYMBOL_DEFAULTS[i]; 1405 } 1406 } 1407 1408 // If monetary decimal or grouping were not explicitly set, then set them to be the same as 1409 // their non-monetary counterparts. 1410 if (numberElements[9] == null) { 1411 numberElements[9] = numberElements[0]; 1412 } 1413 if (numberElements[10] == null) { 1414 numberElements[10] = numberElements[1]; 1415 } 1416 1417 return new CacheData(validLocale, digits, numberElements); 1418 } 1419 initSpacingInfo(CurrencySpacingInfo spcInfo)1420 private void initSpacingInfo(CurrencySpacingInfo spcInfo) { 1421 currencySpcBeforeSym = spcInfo.getBeforeSymbols(); 1422 currencySpcAfterSym = spcInfo.getAfterSymbols(); 1423 } 1424 1425 /** 1426 * Reads the default serializable fields, then if <code>serialVersionOnStream</code> 1427 * is less than 1, initialize <code>monetarySeparator</code> to be 1428 * the same as <code>decimalSeparator</code> and <code>exponential</code> 1429 * to be 'E'. 1430 * Finally, sets serialVersionOnStream back to the maximum allowed value so that 1431 * default serialization will work properly if this object is streamed out again. 1432 */ readObject(ObjectInputStream stream)1433 private void readObject(ObjectInputStream stream) 1434 throws IOException, ClassNotFoundException { 1435 1436 // TODO: it looks to me {dlf} that the serialization code was never updated 1437 // to handle the actual/valid ulocale fields. 1438 1439 stream.defaultReadObject(); 1440 ///CLOVER:OFF 1441 // we don't have data for these old serialized forms any more 1442 if (serialVersionOnStream < 1) { 1443 // Didn't have monetarySeparator or exponential field; 1444 // use defaults. 1445 monetarySeparator = decimalSeparator; 1446 exponential = 'E'; 1447 } 1448 if (serialVersionOnStream < 2) { 1449 padEscape = '*'; 1450 plusSign = '+'; 1451 exponentSeparator = String.valueOf(exponential); 1452 // Although we read the exponential field on stream to create the 1453 // exponentSeparator, we don't do the reverse, since scientific 1454 // notation isn't supported by the old classes, even though the 1455 // symbol is there. 1456 } 1457 ///CLOVER:ON 1458 if (serialVersionOnStream < 3) { 1459 // Resurrected objects from old streams will have no 1460 // locale. There is no 100% fix for this. A 1461 // 90% fix is to construct a mapping of data back to 1462 // locale, perhaps a hash of all our members. This is 1463 // expensive and doesn't seem worth it. 1464 requestedLocale = Locale.getDefault(); 1465 } 1466 if (serialVersionOnStream < 4) { 1467 // use same default behavior as for versions with no Locale 1468 ulocale = ULocale.forLocale(requestedLocale); 1469 } 1470 if (serialVersionOnStream < 5) { 1471 // use the same one for groupingSeparator 1472 monetaryGroupingSeparator = groupingSeparator; 1473 } 1474 if (serialVersionOnStream < 6) { 1475 // Set null to CurrencySpacing related fields. 1476 if (currencySpcBeforeSym == null) { 1477 currencySpcBeforeSym = new String[CURRENCY_SPC_INSERT+1]; 1478 } 1479 if (currencySpcAfterSym == null) { 1480 currencySpcAfterSym = new String[CURRENCY_SPC_INSERT+1]; 1481 } 1482 initSpacingInfo(CurrencyData.CurrencySpacingInfo.DEFAULT); 1483 } 1484 if (serialVersionOnStream < 7) { 1485 // Set minusString,plusString from minusSign,plusSign 1486 if (minusString == null) { 1487 minusString = String.valueOf(minusSign); 1488 } 1489 if (plusString == null) { 1490 plusString = String.valueOf(plusSign); 1491 } 1492 } 1493 if (serialVersionOnStream < 8) { 1494 if (exponentMultiplicationSign == null) { 1495 exponentMultiplicationSign = "\u00D7"; 1496 } 1497 } 1498 if (serialVersionOnStream < 9) { 1499 // String version of digits 1500 if (digitStrings == null) { 1501 digitStrings = new String[10]; 1502 if (digits != null && digits.length == 10) { 1503 zeroDigit = digits[0]; 1504 for (int i = 0; i < 10; i++) { 1505 digitStrings[i] = String.valueOf(digits[i]); 1506 } 1507 } else { 1508 char digit = zeroDigit; 1509 if (digits == null) { 1510 digits = new char[10]; 1511 } 1512 for (int i = 0; i < 10; i++) { 1513 digits[i] = digit; 1514 digitStrings[i] = String.valueOf(digit); 1515 digit++; 1516 } 1517 } 1518 } 1519 1520 // String version of symbols 1521 if (decimalSeparatorString == null) { 1522 decimalSeparatorString = String.valueOf(decimalSeparator); 1523 } 1524 if (groupingSeparatorString == null) { 1525 groupingSeparatorString = String.valueOf(groupingSeparator); 1526 } 1527 if (percentString == null) { 1528 percentString = String.valueOf(percent); 1529 } 1530 if (perMillString == null) { 1531 perMillString = String.valueOf(perMill); 1532 } 1533 if (monetarySeparatorString == null) { 1534 monetarySeparatorString = String.valueOf(monetarySeparator); 1535 } 1536 if (monetaryGroupingSeparatorString == null) { 1537 monetaryGroupingSeparatorString = String.valueOf(monetaryGroupingSeparator); 1538 } 1539 } 1540 1541 serialVersionOnStream = currentSerialVersion; 1542 1543 // recreate 1544 currency = Currency.getInstance(intlCurrencySymbol); 1545 1546 // Refresh digitStrings in order to populate codePointZero 1547 setDigitStrings(digitStrings); 1548 } 1549 1550 /** 1551 * Character used for zero. This remains only for backward compatibility 1552 * purposes. The digits array below is now used to actively store the digits. 1553 * 1554 * @serial 1555 * @see #getZeroDigit 1556 */ 1557 private char zeroDigit; 1558 1559 /** 1560 * Array of characters used for the digits 0-9 in order. 1561 */ 1562 private char digits[]; 1563 1564 /** 1565 * Array of Strings used for the digits 0-9 in order. 1566 * @serial 1567 */ 1568 private String digitStrings[]; 1569 1570 /** 1571 * Dealing with code points is faster than dealing with strings when formatting. Because of 1572 * this, we maintain a value containing the zero code point that is used whenever digitStrings 1573 * represents a sequence of ten code points in order. 1574 * 1575 * <p>If the value stored here is positive, it means that the code point stored in this value 1576 * corresponds to the digitStrings array, and codePointZero can be used instead of the 1577 * digitStrings array for the purposes of efficient formatting; if -1, then digitStrings does 1578 * *not* contain a sequence of code points, and it must be used directly. 1579 * 1580 * <p>It is assumed that codePointZero always shadows the value in digitStrings. codePointZero 1581 * should never be set directly; rather, it should be updated only when digitStrings mutates. 1582 * That is, the flow of information is digitStrings -> codePointZero, not the other way. 1583 */ 1584 private transient int codePointZero; 1585 1586 /** 1587 * Character used for thousands separator. 1588 * 1589 * @serial 1590 * @see #getGroupingSeparator 1591 */ 1592 private char groupingSeparator; 1593 1594 /** 1595 * String used for thousands separator. 1596 * @serial 1597 */ 1598 private String groupingSeparatorString; 1599 1600 /** 1601 * Character used for decimal sign. 1602 * 1603 * @serial 1604 * @see #getDecimalSeparator 1605 */ 1606 private char decimalSeparator; 1607 1608 /** 1609 * String used for decimal sign. 1610 * @serial 1611 */ 1612 private String decimalSeparatorString; 1613 1614 /** 1615 * Character used for mille percent sign. 1616 * 1617 * @serial 1618 * @see #getPerMill 1619 */ 1620 private char perMill; 1621 1622 /** 1623 * String used for mille percent sign. 1624 * @serial 1625 */ 1626 private String perMillString; 1627 1628 /** 1629 * Character used for percent sign. 1630 * @serial 1631 * @see #getPercent 1632 */ 1633 private char percent; 1634 1635 /** 1636 * String used for percent sign. 1637 * @serial 1638 */ 1639 private String percentString; 1640 1641 /** 1642 * Character used for a digit in a pattern. 1643 * 1644 * @serial 1645 * @see #getDigit 1646 */ 1647 private char digit; 1648 1649 /** 1650 * Character used for a significant digit in a pattern. 1651 * 1652 * @serial 1653 * @see #getSignificantDigit 1654 */ 1655 private char sigDigit; 1656 1657 /** 1658 * Character used to separate positive and negative subpatterns 1659 * in a pattern. 1660 * 1661 * @serial 1662 * @see #getPatternSeparator 1663 */ 1664 private char patternSeparator; 1665 1666 /** 1667 * Character used to represent infinity. 1668 * @serial 1669 * @see #getInfinity 1670 */ 1671 private String infinity; 1672 1673 /** 1674 * Character used to represent NaN. 1675 * @serial 1676 * @see #getNaN 1677 */ 1678 private String NaN; 1679 1680 /** 1681 * Character used to represent minus sign. 1682 * @serial 1683 * @see #getMinusSign 1684 */ 1685 private char minusSign; 1686 1687 /** 1688 * String versions of minus sign. 1689 * @serial 1690 */ 1691 private String minusString; 1692 1693 /** 1694 * The character used to indicate a plus sign. 1695 * @serial 1696 */ 1697 private char plusSign; 1698 1699 /** 1700 * String versions of plus sign. 1701 * @serial 1702 */ 1703 private String plusString; 1704 1705 /** 1706 * String denoting the local currency, e.g. "$". 1707 * @serial 1708 * @see #getCurrencySymbol 1709 */ 1710 private String currencySymbol; 1711 1712 /** 1713 * International string denoting the local currency, e.g. "USD". 1714 * @serial 1715 * @see #getInternationalCurrencySymbol 1716 */ 1717 private String intlCurrencySymbol; 1718 1719 /** 1720 * The decimal separator character used when formatting currency values. 1721 * @serial 1722 * @see #getMonetaryDecimalSeparator 1723 */ 1724 private char monetarySeparator; // Field new in JDK 1.1.6 1725 1726 /** 1727 * The decimal separator string used when formatting currency values. 1728 * @serial 1729 */ 1730 private String monetarySeparatorString; 1731 1732 /** 1733 * The grouping separator character used when formatting currency values. 1734 * @serial 1735 * @see #getMonetaryGroupingSeparator 1736 */ 1737 private char monetaryGroupingSeparator; // Field new in JDK 1.1.6 1738 1739 /** 1740 * The grouping separator string used when formatting currency values. 1741 * @serial 1742 */ 1743 private String monetaryGroupingSeparatorString; 1744 1745 /** 1746 * The character used to distinguish the exponent in a number formatted 1747 * in exponential notation, e.g. 'E' for a number such as "1.23E45". 1748 * <p> 1749 * Note that this field has been superseded by <code>exponentSeparator</code>. 1750 * It is retained for backward compatibility. 1751 * 1752 * @serial 1753 */ 1754 private char exponential; // Field new in JDK 1.1.6 1755 1756 /** 1757 * The string used to separate the mantissa from the exponent. 1758 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4. 1759 * <p> 1760 * Note that this supersedes the <code>exponential</code> field. 1761 * 1762 * @serial 1763 */ 1764 private String exponentSeparator; 1765 1766 /** 1767 * The character used to indicate a padding character in a format, 1768 * e.g., '*' in a pattern such as "$*_#,##0.00". 1769 * @serial 1770 */ 1771 private char padEscape; 1772 1773 /** 1774 * The locale for which this object was constructed. Set to the 1775 * default locale for objects resurrected from old streams. 1776 */ 1777 private Locale requestedLocale; 1778 1779 /** 1780 * The requested ULocale. We keep the old locale for serialization compatibility. 1781 */ 1782 private ULocale ulocale; 1783 1784 /** 1785 * Exponent multiplication sign. e.g "x" 1786 * @serial 1787 */ 1788 private String exponentMultiplicationSign = null; 1789 1790 // Proclaim JDK 1.1 FCS compatibility 1791 private static final long serialVersionUID = 5772796243397350300L; 1792 1793 // The internal serial version which says which version was written 1794 // - 0 (default) for version up to JDK 1.1.5 1795 // - 1 for version from JDK 1.1.6, which includes two new fields: 1796 // monetarySeparator and exponential. 1797 // - 2 for version from AlphaWorks, which includes 3 new fields: 1798 // padEscape, exponentSeparator, and plusSign. 1799 // - 3 for ICU 2.2, which includes the locale field 1800 // - 4 for ICU 3.2, which includes the ULocale field 1801 // - 5 for ICU 3.6, which includes the monetaryGroupingSeparator field 1802 // - 6 for ICU 4.2, which includes the currencySpc* fields 1803 // - 7 for ICU 52, which includes the minusString and plusString fields 1804 // - 8 for ICU 54, which includes exponentMultiplicationSign field. 1805 // - 9 for ICU 58, which includes a series of String symbol fields. 1806 private static final int currentSerialVersion = 8; 1807 1808 /** 1809 * Describes the version of <code>DecimalFormatSymbols</code> present on the stream. 1810 * Possible values are: 1811 * <ul> 1812 * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6. 1813 * 1814 * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which includes 1815 * two new fields: <code>monetarySeparator</code> and <code>exponential</code>. 1816 * <li><b>2</b>: Version for AlphaWorks. Adds padEscape, exponentSeparator, 1817 * and plusSign. 1818 * <li><b>3</b>: Version for ICU 2.2, which adds locale. 1819 * <li><b>4</b>: Version for ICU 3.2, which adds ulocale. 1820 * <li><b>5</b>: Version for ICU 3.6, which adds monetaryGroupingSeparator. 1821 * <li><b>6</b>: Version for ICU 4.2, which adds currencySpcBeforeSym and 1822 * currencySpcAfterSym. 1823 * <li><b>7</b>: Version for ICU 52, which adds minusString and plusString. 1824 * </ul> 1825 * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format 1826 * (corresponding to the highest allowable <code>serialVersionOnStream</code>) 1827 * is always written. 1828 * 1829 * @serial 1830 */ 1831 private int serialVersionOnStream = currentSerialVersion; 1832 1833 /** 1834 * cache to hold the NumberElements of a Locale. 1835 */ 1836 private static final CacheBase<ULocale, CacheData, Void> cachedLocaleData = 1837 new SoftCache<ULocale, CacheData, Void>() { 1838 @Override 1839 protected CacheData createInstance(ULocale locale, Void unused) { 1840 return DecimalFormatSymbols.loadData(locale); 1841 } 1842 }; 1843 1844 /** 1845 * 1846 */ 1847 private String currencyPattern = null; 1848 1849 // -------- BEGIN ULocale boilerplate -------- 1850 1851 /** 1852 * <strong>[icu]</strong> Returns the locale that was used to create this object, or null. 1853 * This may may differ from the locale requested at the time of 1854 * this object's creation. For example, if an object is created 1855 * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be 1856 * drawn from <tt>en</tt> (the <i>actual</i> locale), and 1857 * <tt>en_US</tt> may be the most specific locale that exists (the 1858 * <i>valid</i> locale). 1859 * 1860 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i> 1861 * locale is not, in most cases. 1862 * @param type type of information requested, either {@link 1863 * ohos.global.icu.util.ULocale#VALID_LOCALE} or {@link 1864 * ohos.global.icu.util.ULocale#ACTUAL_LOCALE}. 1865 * @return the information specified by <i>type</i>, or null if 1866 * this object was not constructed from locale data. 1867 * @see ohos.global.icu.util.ULocale 1868 * @see ohos.global.icu.util.ULocale#VALID_LOCALE 1869 * @see ohos.global.icu.util.ULocale#ACTUAL_LOCALE 1870 * @hide draft / provisional / internal are hidden on OHOS 1871 */ getLocale(ULocale.Type type)1872 public final ULocale getLocale(ULocale.Type type) { 1873 return type == ULocale.ACTUAL_LOCALE ? 1874 this.actualLocale : this.validLocale; 1875 } 1876 1877 /** 1878 * <strong>[icu]</strong> Sets information about the locales that were used to create this 1879 * object. If the object was not constructed from locale data, 1880 * both arguments should be set to null. Otherwise, neither 1881 * should be null. The actual locale must be at the same level or 1882 * less specific than the valid locale. This method is intended 1883 * for use by factories or other entities that create objects of 1884 * this class. 1885 * @param valid the most specific locale containing any resource 1886 * data, or null 1887 * @param actual the locale containing data used to construct this 1888 * object, or null 1889 * @see ohos.global.icu.util.ULocale 1890 * @see ohos.global.icu.util.ULocale#VALID_LOCALE 1891 * @see ohos.global.icu.util.ULocale#ACTUAL_LOCALE 1892 */ setLocale(ULocale valid, ULocale actual)1893 final void setLocale(ULocale valid, ULocale actual) { 1894 // Change the following to an assertion later 1895 if ((valid == null) != (actual == null)) { 1896 ///CLOVER:OFF 1897 throw new IllegalArgumentException(); 1898 ///CLOVER:ON 1899 } 1900 // Another check we could do is that the actual locale is at 1901 // the same level or less specific than the valid locale. 1902 this.validLocale = valid; 1903 this.actualLocale = actual; 1904 } 1905 1906 /** 1907 * The most specific locale containing any resource data, or null. 1908 * @see ohos.global.icu.util.ULocale 1909 */ 1910 private ULocale validLocale; 1911 1912 /** 1913 * The locale containing data used to construct this object, or 1914 * null. 1915 * @see ohos.global.icu.util.ULocale 1916 */ 1917 private ULocale actualLocale; 1918 1919 // not serialized, reconstructed from intlCurrencyCode 1920 private transient Currency currency; 1921 1922 // -------- END ULocale boilerplate -------- 1923 1924 private static class CacheData { 1925 final ULocale validLocale; 1926 final String[] digits; 1927 final String[] numberElements; 1928 CacheData(ULocale loc, String[] digits, String[] numberElements)1929 public CacheData(ULocale loc, String[] digits, String[] numberElements) { 1930 validLocale = loc; 1931 this.digits = digits; 1932 this.numberElements = numberElements; 1933 } 1934 } 1935 } 1936