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