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