2 *******************************************************************************
3 * Copyright (C) 1996-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.text;
9 import java.io.IOException;
10 import java.io.ObjectInputStream;
11 import java.io.Serializable;
12 import java.text.ChoiceFormat;
13 import java.util.Arrays;
14 import java.util.Locale;
15 import java.util.MissingResourceException;
17 import com.ibm.icu.impl.CurrencyData;
18 import com.ibm.icu.impl.CurrencyData.CurrencyDisplayInfo;
19 import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
20 import com.ibm.icu.impl.CurrencyData.CurrencySpacingInfo;
21 import com.ibm.icu.impl.ICUCache;
22 import com.ibm.icu.impl.ICUResourceBundle;
23 import com.ibm.icu.impl.SimpleCache;
24 import com.ibm.icu.util.Currency;
25 import com.ibm.icu.util.ULocale;
26 import com.ibm.icu.util.ULocale.Category;
27 import com.ibm.icu.util.UResourceBundle;
30 * {@icuenhanced java.text.DecimalFormatSymbols}.{@icu _usage_}
32 * This class represents the set of symbols (such as the decimal separator, the grouping
33 * separator, and so on) needed by <code>DecimalFormat</code> to format
34 * numbers. <code>DecimalFormat</code> creates for itself an instance of
35 * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any of
36 * these symbols, you can get the <code>DecimalFormatSymbols</code> object from your
37 * <code>DecimalFormat</code> and modify it.
39 * @see java.util.Locale
45 public class DecimalFormatSymbols implements Cloneable, Serializable {
47 * Creates a DecimalFormatSymbols object for the default <code>FORMAT</code> locale.
48 * @see Category#FORMAT
51 public DecimalFormatSymbols() {
52 initialize(ULocale.getDefault(Category.FORMAT));
56 * Creates a DecimalFormatSymbols object for the given locale.
57 * @param locale the locale
60 public DecimalFormatSymbols(Locale locale) {
61 initialize(ULocale.forLocale(locale));
65 * {@icu} Creates a DecimalFormatSymbols object for the given locale.
66 * @param locale the locale
69 public DecimalFormatSymbols(ULocale locale) {
74 * Returns a DecimalFormatSymbols instance for the default locale.
76 * <p><strong>Note:</strong> Unlike
77 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns
78 * <code>new com.ibm.icu.text.DecimalFormatSymbols()</code>. ICU currently does not
79 * support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java 6.
81 * @return A DecimalFormatSymbols instance.
84 public static DecimalFormatSymbols getInstance() {
85 return new DecimalFormatSymbols();
89 * Returns a DecimalFormatSymbols instance for the given locale.
91 * <p><strong>Note:</strong> Unlike
92 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns
93 * <code>new com.ibm.icu.text.DecimalFormatSymbols(locale)</code>. ICU currently does
94 * not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java
97 * @param locale the locale.
98 * @return A DecimalFormatSymbols instance.
101 public static DecimalFormatSymbols getInstance(Locale locale) {
102 return new DecimalFormatSymbols(locale);
106 * Returns a DecimalFormatSymbols instance for the given locale.
108 * <p><strong>Note:</strong> Unlike
109 * <code>java.text.DecimalFormatSymbols#getInstance</code>, this method simply returns
110 * <code>new com.ibm.icu.text.DecimalFormatSymbols(locale)</code>. ICU currently does
111 * not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in Java
114 * @param locale the locale.
115 * @return A DecimalFormatSymbols instance.
118 public static DecimalFormatSymbols getInstance(ULocale locale) {
119 return new DecimalFormatSymbols(locale);
123 * Returns an array of all locales for which the <code>getInstance</code> methods of
124 * this class can return localized instances.
126 * <p><strong>Note:</strong> Unlike
127 * <code>java.text.DecimalFormatSymbols#getAvailableLocales</code>, this method simply
128 * returns the array of <code>Locale</code>s available for this class. ICU currently
129 * does not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in
132 * @return An array of <code>Locale</code>s for which localized
133 * <code>DecimalFormatSymbols</code> instances are available.
136 public static Locale[] getAvailableLocales() {
137 return ICUResourceBundle.getAvailableLocales();
141 * {@icu} Returns an array of all locales for which the <code>getInstance</code>
142 * methods of this class can return localized instances.
144 * <p><strong>Note:</strong> Unlike
145 * <code>java.text.DecimalFormatSymbols#getAvailableLocales</code>, this method simply
146 * returns the array of <code>ULocale</code>s available in this class. ICU currently
147 * does not support <code>DecimalFormatSymbolsProvider</code>, which was introduced in
150 * @return An array of <code>ULocale</code>s for which localized
151 * <code>DecimalFormatSymbols</code> instances are available.
152 * @stable ICU 3.8 (retain)
153 * @provisional This API might change or be removed in a future release.
155 public static ULocale[] getAvailableULocales() {
156 return ICUResourceBundle.getAvailableULocales();
161 * Returns the character used for zero. Different for Arabic, etc.
162 * @return the character
165 public char getZeroDigit() {
166 if ( digits != null ) {
173 * Returns the array of characters used as digits, in order from 0 through 9
177 public char[] getDigits() {
178 if ( digits != null ) {
179 return digits.clone();
181 char [] digitArray = new char[10];
182 for ( int i = 0 ; i < 10 ; i++ ) {
183 digitArray[i] = (char) (zeroDigit + i);
190 * Returns the array of characters used as digits, in order from 0 through 9
191 * Package private method - don't need to defensively copy.
194 char[] getDigitsLocal() {
195 if ( digits != null ) {
198 char [] digitArray = new char[10];
199 for ( int i = 0 ; i < 10 ; i++ ) {
200 digitArray[i] = (char) (zeroDigit + i);
207 * Sets the character used for zero.
208 * @param zeroDigit the zero character.
211 public void setZeroDigit(char zeroDigit) {
212 if ( digits != null ) {
213 this.digits[0] = zeroDigit;
214 if (Character.digit(zeroDigit,10) == 0) {
215 for ( int i = 1 ; i < 10 ; i++ ) {
216 this.digits[i] = (char)(zeroDigit+i);
220 this.zeroDigit = zeroDigit;
225 * Returns the character used to represent a significant digit in a pattern.
226 * @return the significant digit pattern character
229 public char getSignificantDigit() {
234 * Sets the character used to represent a significant digit in a pattern.
235 * @param sigDigit the significant digit pattern character
238 public void setSignificantDigit(char sigDigit) {
239 this.sigDigit = sigDigit;
243 * Returns the character used for thousands separator. Different for French, etc.
244 * @return the thousands character
247 public char getGroupingSeparator() {
248 return groupingSeparator;
252 * Sets the character used for thousands separator. Different for French, etc.
253 * @param groupingSeparator the thousands character
256 public void setGroupingSeparator(char groupingSeparator) {
257 this.groupingSeparator = groupingSeparator;
261 * Returns the character used for decimal sign. Different for French, etc.
262 * @return the decimal character
265 public char getDecimalSeparator() {
266 return decimalSeparator;
270 * Sets the character used for decimal sign. Different for French, etc.
271 * @param decimalSeparator the decimal character
274 public void setDecimalSeparator(char decimalSeparator) {
275 this.decimalSeparator = decimalSeparator;
279 * Returns the character used for mille percent sign. Different for Arabic, etc.
280 * @return the mille percent character
283 public char getPerMill() {
288 * Sets the character used for mille percent sign. Different for Arabic, etc.
289 * @param perMill the mille percent character
292 public void setPerMill(char perMill) {
293 this.perMill = perMill;
297 * Returns the character used for percent sign. Different for Arabic, etc.
298 * @return the percent character
301 public char getPercent() {
306 * Sets the character used for percent sign. Different for Arabic, etc.
307 * @param percent the percent character
310 public void setPercent(char percent) {
311 this.percent = percent;
315 * Returns the character used for a digit in a pattern.
316 * @return the digit pattern character
319 public char getDigit() {
324 * Sets the character used for a digit in a pattern.
325 * @param digit the digit pattern character
328 public void setDigit(char digit) {
333 * Returns the character used to separate positive and negative subpatterns
335 * @return the pattern separator character
338 public char getPatternSeparator() {
339 return patternSeparator;
343 * Sets the character used to separate positive and negative subpatterns
345 * @param patternSeparator the pattern separator character
348 public void setPatternSeparator(char patternSeparator) {
349 this.patternSeparator = patternSeparator;
353 * Returns the String used to represent infinity. Almost always left
355 * @return the Infinity string
358 //Bug 4194173 [Richard/GCL]
360 public String getInfinity() {
365 * Sets the String used to represent infinity. Almost always left
367 * @param infinity the Infinity String
370 public void setInfinity(String infinity) {
371 this.infinity = infinity;
375 * Returns the String used to represent NaN. Almost always left
377 * @return the NaN String
380 //Bug 4194173 [Richard/GCL]
381 public String getNaN() {
386 * Sets the String used to represent NaN. Almost always left
388 * @param NaN the NaN String
391 public void setNaN(String NaN) {
396 * Returns the character used to represent minus sign. If no explicit
397 * negative format is specified, one is formed by prefixing
398 * minusSign to the positive format.
399 * @return the minus sign character
402 public char getMinusSign() {
407 * Returns the string used to represent minus sign.
408 * @return the minus sign string
410 * @deprecated This API is ICU internal only.
412 public String getMinusString() {
417 * Sets the character used to represent minus sign. If no explicit
418 * negative format is specified, one is formed by prefixing
419 * minusSign to the positive format.
420 * @param minusSign the minus sign character
423 public void setMinusSign(char minusSign) {
424 this.minusSign = minusSign;
425 // Also updates minusString
426 char[] minusArray = { minusSign };
427 minusString = new String(minusArray);
431 * Returns the string denoting the local currency.
432 * @return the local currency String.
435 public String getCurrencySymbol() {
436 return currencySymbol;
440 * Sets the string denoting the local currency.
441 * @param currency the local currency String.
444 public void setCurrencySymbol(String currency) {
445 currencySymbol = currency;
449 * Returns the international string denoting the local currency.
450 * @return the international string denoting the local currency
453 public String getInternationalCurrencySymbol() {
454 return intlCurrencySymbol;
458 * Sets the international string denoting the local currency.
459 * @param currency the international string denoting the local currency.
462 public void setInternationalCurrencySymbol(String currency) {
463 intlCurrencySymbol = currency;
467 * Returns the currency symbol, for JDK 1.4 compatibility only.
468 * ICU clients should use the Currency API directly.
469 * @return the currency used, or null
472 public Currency getCurrency() {
479 * <p><strong>Note:</strong> ICU does not use the DecimalFormatSymbols for the currency
480 * any more. This API is present for API compatibility only.
482 * <p>This also sets the currency symbol attribute to the currency's symbol
483 * in the DecimalFormatSymbols' locale, and the international currency
484 * symbol attribute to the currency's ISO 4217 currency code.
486 * @param currency the new currency to be used
487 * @throws NullPointerException if <code>currency</code> is null
488 * @see #setCurrencySymbol
489 * @see #setInternationalCurrencySymbol
493 public void setCurrency(Currency currency) {
494 if (currency == null) {
495 throw new NullPointerException();
497 this.currency = currency;
498 intlCurrencySymbol = currency.getCurrencyCode();
499 currencySymbol = currency.getSymbol(requestedLocale);
503 * Returns the monetary decimal separator.
504 * @return the monetary decimal separator character
507 public char getMonetaryDecimalSeparator() {
508 return monetarySeparator;
512 * {@icu} Returns the monetary grouping separator.
513 * @return the monetary grouping separator character
516 public char getMonetaryGroupingSeparator() {
517 return monetaryGroupingSeparator;
521 * Internal API for NumberFormat
522 * @return String currency pattern string
524 String getCurrencyPattern() {
525 return currencyPattern;
529 * Sets the monetary decimal separator.
530 * @param sep the monetary decimal separator character
533 public void setMonetaryDecimalSeparator(char sep) {
534 monetarySeparator = sep;
538 * Sets the monetary decimal separator.
539 * @param sep the monetary decimal separator character
542 public void setMonetaryGroupingSeparator(char sep) {
543 monetaryGroupingSeparator = sep;
547 * {@icu} Returns the string used to separate the mantissa from the exponent.
548 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
549 * @return the localized exponent symbol, used in localized patterns
550 * and formatted strings
551 * @see #setExponentSeparator
554 public String getExponentSeparator() {
555 return exponentSeparator;
559 * {@icu} Sets the string used to separate the mantissa from the exponent.
560 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
561 * @param exp the localized exponent symbol, used in localized patterns
562 * and formatted strings
563 * @see #getExponentSeparator
566 public void setExponentSeparator(String exp) {
567 exponentSeparator = exp;
571 * {@icu} Returns the localized plus sign.
572 * @return the plus sign, used in localized patterns and formatted
579 public char getPlusSign() {
584 * Returns the string used to represent plus sign.
585 * @return the plus sign string
587 * @deprecated This API is ICU internal only.
589 public String getPlusString() {
594 * {@icu} Sets the localized plus sign.
595 * @param plus the plus sign, used in localized patterns and formatted
602 public void setPlusSign(char plus) {
604 // Also updates plusString
605 char[] plusArray = { plusSign };
606 plusString = new String(plusArray);
610 * {@icu} Returns the character used to pad numbers out to a specified width. This is
611 * not the pad character itself; rather, it is the special pattern character
612 * <em>preceding</em> the pad character. In the pattern "*_#,##0", '*' is the pad
613 * escape, and '_' is the pad character.
614 * @return the character
616 * @see DecimalFormat#getFormatWidth
617 * @see DecimalFormat#getPadPosition
618 * @see DecimalFormat#getPadCharacter
621 public char getPadEscape() {
626 * {@icu} Sets the character used to pad numbers out to a specified width. This is not
627 * the pad character itself; rather, it is the special pattern character
628 * <em>preceding</em> the pad character. In the pattern "*_#,##0", '*' is the pad
629 * escape, and '_' is the pad character.
631 * @see DecimalFormat#setFormatWidth
632 * @see DecimalFormat#setPadPosition
633 * @see DecimalFormat#setPadCharacter
636 public void setPadEscape(char c) {
641 * {@icu} Indicates the currency match pattern used in {@link #getPatternForCurrencySpacing}.
644 public static final int CURRENCY_SPC_CURRENCY_MATCH = 0;
647 * {@icu} Indicates the surrounding match pattern used in {@link
648 * #getPatternForCurrencySpacing}.
651 public static final int CURRENCY_SPC_SURROUNDING_MATCH = 1;
654 * {@icu} Indicates the insertion value used in {@link #getPatternForCurrencySpacing}.
657 public static final int CURRENCY_SPC_INSERT = 2;
659 private String[] currencySpcBeforeSym;
660 private String[] currencySpcAfterSym;
663 * {@icu} Returns the desired currency spacing value. Original values come from ICU's
664 * CLDR data based on the locale provided during construction, and can be null. These
665 * values govern what and when text is inserted between a currency code/name/symbol
666 * and the currency amount when formatting money.
668 * <p>For more information, see <a href="http://www.unicode.org/reports/tr35/#Currencies"
669 * >UTS#35 section 5.10.2</a>.
671 * <p><strong>Note:</strong> ICU4J does not currently use this information.
673 * @param itemType one of CURRENCY_SPC_CURRENCY_MATCH, CURRENCY_SPC_SURROUNDING_MATCH
674 * or CURRENCY_SPC_INSERT
675 * @param beforeCurrency true to get the <code>beforeCurrency</code> values, false
676 * to get the <code>afterCurrency</code> values.
677 * @return the value, or null.
678 * @see #setPatternForCurrencySpacing(int, boolean, String)
681 public String getPatternForCurrencySpacing(int itemType, boolean beforeCurrency) {
682 if (itemType < CURRENCY_SPC_CURRENCY_MATCH ||
683 itemType > CURRENCY_SPC_INSERT ) {
684 throw new IllegalArgumentException("unknown currency spacing: " + itemType);
686 if (beforeCurrency) {
687 return currencySpcBeforeSym[itemType];
689 return currencySpcAfterSym[itemType];
693 * {@icu} Sets the indicated currency spacing pattern or value. See {@link
694 * #getPatternForCurrencySpacing} for more information.
696 * <p>Values for currency match and surrounding match must be {@link
697 * com.ibm.icu.text.UnicodeSet} patterns. Values for insert can be any string.
699 * <p><strong>Note:</strong> ICU4J does not currently use this information.
701 * @param itemType one of CURRENCY_SPC_CURRENCY_MATCH, CURRENCY_SPC_SURROUNDING_MATCH
702 * or CURRENCY_SPC_INSERT
703 * @param beforeCurrency true if the pattern is for before the currency symbol.
704 * false if the pattern is for after it.
705 * @param pattern string to override current setting; can be null.
706 * @see #getPatternForCurrencySpacing(int, boolean)
709 public void setPatternForCurrencySpacing(int itemType, boolean beforeCurrency, String pattern) {
710 if (itemType < CURRENCY_SPC_CURRENCY_MATCH ||
711 itemType > CURRENCY_SPC_INSERT ) {
712 throw new IllegalArgumentException("unknown currency spacing: " + itemType);
714 if (beforeCurrency) {
715 currencySpcBeforeSym[itemType] = pattern;
717 currencySpcAfterSym[itemType] = pattern;
722 * Returns the locale for which this object was constructed.
723 * @return the locale for which this object was constructed
726 public Locale getLocale() {
727 return requestedLocale;
731 * Returns the locale for which this object was constructed.
732 * @return the locale for which this object was constructed
735 public ULocale getULocale() {
743 public Object clone() {
745 return (DecimalFormatSymbols) super.clone();
746 // other fields are bit-copied
747 } catch (CloneNotSupportedException e) {
749 throw new IllegalStateException();
758 public boolean equals(Object obj) {
759 if (!(obj instanceof DecimalFormatSymbols)) {
765 DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
766 for (int i = 0; i <= CURRENCY_SPC_INSERT; i++) {
767 if (!currencySpcBeforeSym[i].equals(other.currencySpcBeforeSym[i])) {
770 if (!currencySpcAfterSym[i].equals(other.currencySpcAfterSym[i])) {
775 if ( other.digits == null ) {
776 for (int i = 0 ; i < 10 ; i++) {
777 if (digits[i] != other.zeroDigit + i) {
781 } else if (!Arrays.equals(digits,other.digits)) {
786 groupingSeparator == other.groupingSeparator &&
787 decimalSeparator == other.decimalSeparator &&
788 percent == other.percent &&
789 perMill == other.perMill &&
790 digit == other.digit &&
791 minusSign == other.minusSign &&
792 minusString.equals(other.minusString) &&
793 patternSeparator == other.patternSeparator &&
794 infinity.equals(other.infinity) &&
795 NaN.equals(other.NaN) &&
796 currencySymbol.equals(other.currencySymbol) &&
797 intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
798 padEscape == other.padEscape &&
799 plusSign == other.plusSign &&
800 plusString.equals(other.plusString) &&
801 exponentSeparator.equals(other.exponentSeparator) &&
802 monetarySeparator == other.monetarySeparator &&
803 monetaryGroupingSeparator == other.monetaryGroupingSeparator);
810 public int hashCode() {
811 int result = digits[0];
812 result = result * 37 + groupingSeparator;
813 result = result * 37 + decimalSeparator;
818 * Check for bidi marks: LRM, RLM, ALM
820 private static boolean isBidiMark(char c) {
821 return (c=='\u200E' || c=='\u200F' || c=='\u061C');
825 * Initializes the symbols from the LocaleElements resource bundle.
826 * Note: The organization of LocaleElements badly needs to be
829 private void initialize( ULocale locale ) {
830 this.requestedLocale = locale.toLocale();
831 this.ulocale = locale;
834 // Attempt to set the zero digit based on the numbering system for the locale requested
835 NumberingSystem ns = NumberingSystem.getInstance(locale);
836 digits = new char[10];
837 if ( ns != null && ns.getRadix() == 10 && !ns.isAlgorithmic() &&
838 NumberingSystem.isValidDigitString(ns.getDescription())) {
839 String digitString = ns.getDescription();
840 digits[0] = digitString.charAt(0);
841 digits[1] = digitString.charAt(1);
842 digits[2] = digitString.charAt(2);
843 digits[3] = digitString.charAt(3);
844 digits[4] = digitString.charAt(4);
845 digits[5] = digitString.charAt(5);
846 digits[6] = digitString.charAt(6);
847 digits[7] = digitString.charAt(7);
848 digits[8] = digitString.charAt(8);
849 digits[9] = digitString.charAt(9);
850 nsName = ns.getName();
852 digits[0] = DecimalFormat.PATTERN_ZERO_DIGIT;
853 digits[1] = DecimalFormat.PATTERN_ONE_DIGIT;
854 digits[2] = DecimalFormat.PATTERN_TWO_DIGIT;
855 digits[3] = DecimalFormat.PATTERN_THREE_DIGIT;
856 digits[4] = DecimalFormat.PATTERN_FOUR_DIGIT;
857 digits[5] = DecimalFormat.PATTERN_FIVE_DIGIT;
858 digits[6] = DecimalFormat.PATTERN_SIX_DIGIT;
859 digits[7] = DecimalFormat.PATTERN_SEVEN_DIGIT;
860 digits[8] = DecimalFormat.PATTERN_EIGHT_DIGIT;
861 digits[9] = DecimalFormat.PATTERN_NINE_DIGIT;
862 nsName = "latn"; // Default numbering system
865 /* try the cache first */
866 String[][] data = cachedLocaleData.get(locale);
867 String[] numberElements;
868 if (data == null) { /* cache miss */
869 data = new String[1][];
870 ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.
871 getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
872 boolean isLatn = nsName.equals("latn");
873 String baseKey = "NumberElements/" + nsName + "/symbols/";
874 String latnKey = "NumberElements/latn/symbols/";
875 String[] symbolKeys = { "decimal", "group", "list", "percentSign", "minusSign", "plusSign", "exponential", "perMille", "infinity", "nan", "currencyDecimal", "currencyGroup" };
876 String[] fallbackElements = { ".", ",", ";", "%", "-", "+", "E", "\u2030", "\u221e", "NaN", null, null };
877 String[] symbolsArray = new String[symbolKeys.length];
878 for ( int i = 0 ; i < symbolKeys.length; i++ ) {
880 symbolsArray[i] = rb.getStringWithFallback(baseKey+symbolKeys[i]);
881 } catch (MissingResourceException ex) {
882 if (!isLatn) { // Fall back to latn numbering system for symbols if desired symbol isn't found.
884 symbolsArray[i] = rb.getStringWithFallback(latnKey+symbolKeys[i]);
885 } catch (MissingResourceException ex1) {
886 symbolsArray[i] = fallbackElements[i];
889 symbolsArray[i] = fallbackElements[i];
894 data[0] = symbolsArray;
896 cachedLocaleData.put(locale, data);
898 numberElements = data[0];
900 ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
901 getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
903 // TODO: Determine actual and valid locale correctly.
904 ULocale uloc = r.getULocale();
905 setLocale(uloc, uloc);
908 decimalSeparator = numberElements[0].charAt(0);
909 groupingSeparator = numberElements[1].charAt(0);
910 patternSeparator = numberElements[2].charAt(0);
911 percent = numberElements[3].charAt(0);
912 minusString = numberElements[4];
913 minusSign = (minusString.length() > 1 && isBidiMark(minusString.charAt(0)))? minusString.charAt(1): minusString.charAt(0);
914 plusString = numberElements[5];
915 plusSign = (plusString.length() > 1 && isBidiMark(plusString.charAt(0)))? plusString.charAt(1): plusString.charAt(0);
916 exponentSeparator = numberElements[6];
917 perMill = numberElements[7].charAt(0);
918 infinity = numberElements[8];
919 NaN = numberElements[9];
921 if ( numberElements[10] != null) {
922 monetarySeparator = numberElements[10].charAt(0);
924 monetarySeparator = decimalSeparator;
927 if ( numberElements[11] != null) {
928 monetaryGroupingSeparator = numberElements[11].charAt(0);
930 monetaryGroupingSeparator = groupingSeparator;
933 digit = DecimalFormat.PATTERN_DIGIT; // Localized pattern character no longer in CLDR
934 padEscape = DecimalFormat.PATTERN_PAD_ESCAPE;
935 sigDigit = DecimalFormat.PATTERN_SIGNIFICANT_DIGIT;
938 CurrencyDisplayInfo info = CurrencyData.provider.getInstance(locale, true);
940 // Obtain currency data from the currency API. This is strictly
941 // for backward compatibility; we don't use DecimalFormatSymbols
942 // for currency data anymore.
943 String currname = null;
944 currency = Currency.getInstance(locale);
945 if (currency != null) {
946 intlCurrencySymbol = currency.getCurrencyCode();
947 boolean[] isChoiceFormat = new boolean[1];
948 currname = currency.getName(locale, Currency.SYMBOL_NAME, isChoiceFormat);
949 // If this is a ChoiceFormat currency, then format an
950 // arbitrary value; pick something != 1; more common.
951 currencySymbol = isChoiceFormat[0]
952 ? new ChoiceFormat(currname).format(2.0)
954 CurrencyFormatInfo fmtInfo = info.getFormatInfo(intlCurrencySymbol);
955 if (fmtInfo != null) {
956 currencyPattern = fmtInfo.currencyPattern;
957 monetarySeparator = fmtInfo.monetarySeparator;
958 monetaryGroupingSeparator = fmtInfo.monetaryGroupingSeparator;
961 intlCurrencySymbol = "XXX";
962 currencySymbol = "\u00A4"; // 'OX' currency symbol
966 // Get currency spacing data.
967 currencySpcBeforeSym = new String[3];
968 currencySpcAfterSym = new String[3];
969 initSpacingInfo(info.getSpacingInfo());
972 private void initSpacingInfo(CurrencySpacingInfo spcInfo) {
973 currencySpcBeforeSym[CURRENCY_SPC_CURRENCY_MATCH] = spcInfo.beforeCurrencyMatch;
974 currencySpcBeforeSym[CURRENCY_SPC_SURROUNDING_MATCH] = spcInfo.beforeContextMatch;
975 currencySpcBeforeSym[CURRENCY_SPC_INSERT] = spcInfo.beforeInsert;
976 currencySpcAfterSym[CURRENCY_SPC_CURRENCY_MATCH] = spcInfo.afterCurrencyMatch;
977 currencySpcAfterSym[CURRENCY_SPC_SURROUNDING_MATCH] = spcInfo.afterContextMatch;
978 currencySpcAfterSym[CURRENCY_SPC_INSERT] = spcInfo.afterInsert;
982 * Reads the default serializable fields, then if <code>serialVersionOnStream</code>
983 * is less than 1, initialize <code>monetarySeparator</code> to be
984 * the same as <code>decimalSeparator</code> and <code>exponential</code>
986 * Finally, sets serialVersionOnStream back to the maximum allowed value so that
987 * default serialization will work properly if this object is streamed out again.
989 private void readObject(ObjectInputStream stream)
990 throws IOException, ClassNotFoundException {
992 // TODO: it looks to me {dlf} that the serialization code was never updated
993 // to handle the actual/valid ulocale fields.
995 stream.defaultReadObject();
997 // we don't have data for these old serialized forms any more
998 if (serialVersionOnStream < 1) {
999 // Didn't have monetarySeparator or exponential field;
1001 monetarySeparator = decimalSeparator;
1004 if (serialVersionOnStream < 2) {
1005 padEscape = DecimalFormat.PATTERN_PAD_ESCAPE;
1006 plusSign = DecimalFormat.PATTERN_PLUS_SIGN;
1007 exponentSeparator = String.valueOf(exponential);
1008 // Although we read the exponential field on stream to create the
1009 // exponentSeparator, we don't do the reverse, since scientific
1010 // notation isn't supported by the old classes, even though the
1014 if (serialVersionOnStream < 3) {
1015 // Resurrected objects from old streams will have no
1016 // locale. There is no 100% fix for this. A
1017 // 90% fix is to construct a mapping of data back to
1018 // locale, perhaps a hash of all our members. This is
1019 // expensive and doesn't seem worth it.
1020 requestedLocale = Locale.getDefault();
1022 if (serialVersionOnStream < 4) {
1023 // use same default behavior as for versions with no Locale
1024 ulocale = ULocale.forLocale(requestedLocale);
1026 if (serialVersionOnStream < 5) {
1027 // use the same one for groupingSeparator
1028 monetaryGroupingSeparator = groupingSeparator;
1030 if (serialVersionOnStream < 6) {
1031 // Set null to CurrencySpacing related fields.
1032 if (currencySpcBeforeSym == null) {
1033 currencySpcBeforeSym = new String[CURRENCY_SPC_INSERT+1];
1035 if (currencySpcAfterSym == null) {
1036 currencySpcAfterSym = new String[CURRENCY_SPC_INSERT+1];
1038 initSpacingInfo(CurrencyData.CurrencySpacingInfo.DEFAULT);
1040 if (serialVersionOnStream < 7) {
1041 // Set minusString,plusString from minusSign,plusSign
1042 if (minusString == null) {
1043 char[] minusArray = { minusSign };
1044 minusString = new String(minusArray);
1046 if (plusString == null) {
1047 char[] plusArray = { plusSign };
1048 plusString = new String(plusArray);
1051 serialVersionOnStream = currentSerialVersion;
1054 currency = Currency.getInstance(intlCurrencySymbol);
1058 * Character used for zero. This remains only for backward compatibility
1059 * purposes. The digits array below is now used to actively store the digits.
1062 * @see #getZeroDigit
1064 private char zeroDigit;
1067 * Array of characters used for the digits 0-9 in order.
1070 private char digits[];
1073 * Character used for thousands separator.
1076 * @see #getGroupingSeparator
1078 private char groupingSeparator;
1081 * Character used for decimal sign.
1084 * @see #getDecimalSeparator
1086 private char decimalSeparator;
1089 * Character used for mille percent sign.
1094 private char perMill;
1097 * Character used for percent sign.
1101 private char percent;
1104 * Character used for a digit in a pattern.
1112 * Character used for a significant digit in a pattern.
1115 * @see #getSignificantDigit
1117 private char sigDigit;
1120 * Character used to separate positive and negative subpatterns
1124 * @see #getPatternSeparator
1126 private char patternSeparator;
1129 * Character used to represent infinity.
1133 private String infinity;
1136 * Character used to represent NaN.
1143 * Character used to represent minus sign.
1145 * @see #getMinusSign
1147 private char minusSign;
1150 * String denoting the local currency, e.g. "$".
1152 * @see #getCurrencySymbol
1154 private String currencySymbol;
1157 * International string denoting the local currency, e.g. "USD".
1159 * @see #getInternationalCurrencySymbol
1161 private String intlCurrencySymbol;
1164 * The decimal separator used when formatting currency values.
1166 * @see #getMonetaryDecimalSeparator
1168 private char monetarySeparator; // Field new in JDK 1.1.6
1171 * The decimal separator used when formatting currency values.
1173 * @see #getMonetaryGroupingSeparator
1175 private char monetaryGroupingSeparator; // Field new in JDK 1.1.6
1178 * The character used to distinguish the exponent in a number formatted
1179 * in exponential notation, e.g. 'E' for a number such as "1.23E45".
1181 * Note that this field has been superseded by <code>exponentSeparator</code>.
1182 * It is retained for backward compatibility.
1186 private char exponential; // Field new in JDK 1.1.6
1189 * The string used to separate the mantissa from the exponent.
1190 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
1192 * Note that this supersedes the <code>exponential</code> field.
1197 private String exponentSeparator;
1200 * The character used to indicate a padding character in a format,
1201 * e.g., '*' in a pattern such as "$*_#,##0.00".
1205 private char padEscape;
1208 * The character used to indicate a plus sign.
1212 private char plusSign;
1215 * The locale for which this object was constructed. Set to the
1216 * default locale for objects resurrected from old streams.
1219 private Locale requestedLocale;
1222 * The requested ULocale. We keep the old locale for serialization compatibility.
1225 private ULocale ulocale;
1228 * String versions of some number symbols.
1232 private String minusString = null;
1233 private String plusString = null;
1235 // Proclaim JDK 1.1 FCS compatibility
1236 private static final long serialVersionUID = 5772796243397350300L;
1238 // The internal serial version which says which version was written
1239 // - 0 (default) for version up to JDK 1.1.5
1240 // - 1 for version from JDK 1.1.6, which includes two new fields:
1241 // monetarySeparator and exponential.
1242 // - 2 for version from AlphaWorks, which includes 3 new fields:
1243 // padEscape, exponentSeparator, and plusSign.
1244 // - 3 for ICU 2.2, which includes the locale field
1245 // - 4 for ICU 3.2, which includes the ULocale field
1246 // - 5 for ICU 3.6, which includes the monetaryGroupingSeparator field
1247 // - 6 for ICU 4.2, which includes the currencySpc* fields
1248 // - 7 for ICU 52, which includes the minusString and plusString fields
1249 private static final int currentSerialVersion = 7;
1252 * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
1253 * Possible values are:
1255 * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
1257 * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which includes
1258 * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
1259 * <li><b>2</b>: Version for AlphaWorks. Adds padEscape, exponentSeparator,
1261 * <li><b>3</b>: Version for ICU 2.2, which adds locale.
1262 * <li><b>4</b>: Version for ICU 3.2, which adds ulocale.
1263 * <li><b>5</b>: Version for ICU 3.6, which adds monetaryGroupingSeparator.
1264 * <li><b>6</b>: Version for ICU 4.2, which adds currencySpcBeforeSym and
1265 * currencySpcAfterSym.
1266 * <li><b>7</b>: Version for ICU 52, which adds minusString and plusString.
1268 * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
1269 * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
1270 * is always written.
1274 private int serialVersionOnStream = currentSerialVersion;
1277 * cache to hold the NumberElements of a Locale.
1279 private static final ICUCache<ULocale, String[][]> cachedLocaleData =
1280 new SimpleCache<ULocale, String[][]>();
1285 private String currencyPattern = null;
1287 // -------- BEGIN ULocale boilerplate --------
1290 * {@icu} Returns the locale that was used to create this object, or null.
1291 * This may may differ from the locale requested at the time of
1292 * this object's creation. For example, if an object is created
1293 * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be
1294 * drawn from <tt>en</tt> (the <i>actual</i> locale), and
1295 * <tt>en_US</tt> may be the most specific locale that exists (the
1296 * <i>valid</i> locale).
1298 * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
1299 * locale is not, in most cases.
1300 * @param type type of information requested, either {@link
1301 * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link
1302 * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}.
1303 * @return the information specified by <i>type</i>, or null if
1304 * this object was not constructed from locale data.
1305 * @see com.ibm.icu.util.ULocale
1306 * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1307 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1308 * @draft ICU 2.8 (retain)
1309 * @provisional This API might change or be removed in a future release.
1311 public final ULocale getLocale(ULocale.Type type) {
1312 return type == ULocale.ACTUAL_LOCALE ?
1313 this.actualLocale : this.validLocale;
1317 * {@icu} Sets information about the locales that were used to create this
1318 * object. If the object was not constructed from locale data,
1319 * both arguments should be set to null. Otherwise, neither
1320 * should be null. The actual locale must be at the same level or
1321 * less specific than the valid locale. This method is intended
1322 * for use by factories or other entities that create objects of
1324 * @param valid the most specific locale containing any resource
1326 * @param actual the locale containing data used to construct this
1328 * @see com.ibm.icu.util.ULocale
1329 * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1330 * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1332 final void setLocale(ULocale valid, ULocale actual) {
1333 // Change the following to an assertion later
1334 if ((valid == null) != (actual == null)) {
1336 throw new IllegalArgumentException();
1339 // Another check we could do is that the actual locale is at
1340 // the same level or less specific than the valid locale.
1341 this.validLocale = valid;
1342 this.actualLocale = actual;
1346 * The most specific locale containing any resource data, or null.
1347 * @see com.ibm.icu.util.ULocale
1349 private ULocale validLocale;
1352 * The locale containing data used to construct this object, or
1354 * @see com.ibm.icu.util.ULocale
1356 private ULocale actualLocale;
1358 // not serialized, reconstructed from intlCurrencyCode
1359 private transient Currency currency;
1361 // -------- END ULocale boilerplate --------