]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
Clean up imports.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / text / DecimalFormatSymbols.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.text;
8
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;
16
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;
28
29 /**
30  * {@icuenhanced java.text.DecimalFormatSymbols}.{@icu _usage_}
31  *
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.
38  *
39  * @see          java.util.Locale
40  * @see          DecimalFormat
41  * @author       Mark Davis
42  * @author       Alan Liu
43  * @stable ICU 2.0
44  */
45 public class DecimalFormatSymbols implements Cloneable, Serializable {
46     /**
47      * Creates a DecimalFormatSymbols object for the default <code>FORMAT</code> locale.
48      * @see Category#FORMAT
49      * @stable ICU 2.0
50      */
51     public DecimalFormatSymbols() {
52         initialize(ULocale.getDefault(Category.FORMAT));
53     }
54
55     /**
56      * Creates a DecimalFormatSymbols object for the given locale.
57      * @param locale the locale
58      * @stable ICU 2.0
59      */
60     public DecimalFormatSymbols(Locale locale) {
61         initialize(ULocale.forLocale(locale));
62     }
63
64     /**
65      * {@icu} Creates a DecimalFormatSymbols object for the given locale.
66      * @param locale the locale
67      * @stable ICU 3.2
68      */
69     public DecimalFormatSymbols(ULocale locale) {
70         initialize(locale);
71     }
72
73     /**
74      * Returns a DecimalFormatSymbols instance for the default locale.
75      *
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.
80      *
81      * @return A DecimalFormatSymbols instance.
82      * @stable ICU 3.8
83      */
84     public static DecimalFormatSymbols getInstance() {
85         return new DecimalFormatSymbols();
86     }
87
88     /**
89      * Returns a DecimalFormatSymbols instance for the given locale.
90      *
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
95      * 6.
96      *
97      * @param locale the locale.
98      * @return A DecimalFormatSymbols instance.
99      * @stable ICU 3.8
100      */
101     public static DecimalFormatSymbols getInstance(Locale locale) {
102         return new DecimalFormatSymbols(locale);
103     }
104
105     /**
106      * Returns a DecimalFormatSymbols instance for the given locale.
107      *
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
112      * 6.
113      *
114      * @param locale the locale.
115      * @return A DecimalFormatSymbols instance.
116      * @stable ICU 3.8
117      */
118     public static DecimalFormatSymbols getInstance(ULocale locale) {
119         return new DecimalFormatSymbols(locale);
120     }
121
122     /**
123      * Returns an array of all locales for which the <code>getInstance</code> methods of
124      * this class can return localized instances.
125      *
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
130      * Java 6.
131      *
132      * @return An array of <code>Locale</code>s for which localized
133      * <code>DecimalFormatSymbols</code> instances are available.
134      * @stable ICU 3.8
135      */
136     public static Locale[] getAvailableLocales() {
137         return ICUResourceBundle.getAvailableLocales();
138     }
139
140     /**
141      * {@icu} Returns an array of all locales for which the <code>getInstance</code>
142      * methods of this class can return localized instances.
143      *
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
148      * Java 6.
149      *
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.
154      */
155     public static ULocale[] getAvailableULocales() {
156         return ICUResourceBundle.getAvailableULocales();
157     }
158
159
160     /**
161      * Returns the character used for zero. Different for Arabic, etc.
162      * @return the character
163      * @stable ICU 2.0
164      */
165     public char getZeroDigit() {
166         if ( digits != null ) {
167             return digits[0];
168         } else {
169             return zeroDigit;
170         }
171     }
172     /**
173      * Returns the array of characters used as digits, in order from 0 through 9
174      * @return The array
175      * @stable ICU 4.6
176      */
177     public char[] getDigits() {
178         if ( digits != null ) {
179             return digits.clone();
180         } else {
181             char [] digitArray = new char[10];
182             for ( int i = 0 ; i < 10 ; i++ ) {
183                 digitArray[i] = (char) (zeroDigit + i);
184             }
185             return digitArray;
186         }
187     }
188     
189     /**
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.
192      * @return The array
193      */
194     char[] getDigitsLocal() {
195         if ( digits != null ) {
196             return digits;
197         } else {
198             char [] digitArray = new char[10];
199             for ( int i = 0 ; i < 10 ; i++ ) {
200                 digitArray[i] = (char) (zeroDigit + i);
201             }
202             return digitArray;
203         }
204     }
205
206     /**
207      * Sets the character used for zero.
208      * @param zeroDigit the zero character.
209      * @stable ICU 2.0
210      */
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);
217                 }
218             }
219         } else {
220             this.zeroDigit = zeroDigit;
221         }
222     }
223
224     /**
225      * Returns the character used to represent a significant digit in a pattern.
226      * @return the significant digit pattern character
227      * @stable ICU 3.0
228      */
229     public char getSignificantDigit() {
230         return sigDigit;
231     }
232
233     /**
234      * Sets the character used to represent a significant digit in a pattern.
235      * @param sigDigit the significant digit pattern character
236      * @stable ICU 3.0
237      */
238     public void setSignificantDigit(char sigDigit) {
239         this.sigDigit = sigDigit;
240     }
241
242     /**
243      * Returns the character used for thousands separator. Different for French, etc.
244      * @return the thousands character
245      * @stable ICU 2.0
246      */
247     public char getGroupingSeparator() {
248         return groupingSeparator;
249     }
250
251     /**
252      * Sets the character used for thousands separator. Different for French, etc.
253      * @param groupingSeparator the thousands character
254      * @stable ICU 2.0
255      */
256     public void setGroupingSeparator(char groupingSeparator) {
257         this.groupingSeparator = groupingSeparator;
258     }
259
260     /**
261      * Returns the character used for decimal sign. Different for French, etc.
262      * @return the decimal character
263      * @stable ICU 2.0
264      */
265     public char getDecimalSeparator() {
266         return decimalSeparator;
267     }
268
269     /**
270      * Sets the character used for decimal sign. Different for French, etc.
271      * @param decimalSeparator the decimal character
272      * @stable ICU 2.0
273      */
274     public void setDecimalSeparator(char decimalSeparator) {
275         this.decimalSeparator = decimalSeparator;
276     }
277
278     /**
279      * Returns the character used for mille percent sign. Different for Arabic, etc.
280      * @return the mille percent character
281      * @stable ICU 2.0
282      */
283     public char getPerMill() {
284         return perMill;
285     }
286
287     /**
288      * Sets the character used for mille percent sign. Different for Arabic, etc.
289      * @param perMill the mille percent character
290      * @stable ICU 2.0
291      */
292     public void setPerMill(char perMill) {
293         this.perMill = perMill;
294     }
295
296     /**
297      * Returns the character used for percent sign. Different for Arabic, etc.
298      * @return the percent character
299      * @stable ICU 2.0
300      */
301     public char getPercent() {
302         return percent;
303     }
304
305     /**
306      * Sets the character used for percent sign. Different for Arabic, etc.
307      * @param percent the percent character
308      * @stable ICU 2.0
309      */
310     public void setPercent(char percent) {
311         this.percent = percent;
312     }
313
314     /**
315      * Returns the character used for a digit in a pattern.
316      * @return the digit pattern character
317      * @stable ICU 2.0
318      */
319     public char getDigit() {
320         return digit;
321     }
322
323     /**
324      * Sets the character used for a digit in a pattern.
325      * @param digit the digit pattern character
326      * @stable ICU 2.0
327      */
328     public void setDigit(char digit) {
329         this.digit = digit;
330     }
331
332     /**
333      * Returns the character used to separate positive and negative subpatterns
334      * in a pattern.
335      * @return the pattern separator character
336      * @stable ICU 2.0
337      */
338     public char getPatternSeparator() {
339         return patternSeparator;
340     }
341
342     /**
343      * Sets the character used to separate positive and negative subpatterns
344      * in a pattern.
345      * @param patternSeparator the pattern separator character
346      * @stable ICU 2.0
347      */
348     public void setPatternSeparator(char patternSeparator) {
349         this.patternSeparator = patternSeparator;
350     }
351
352     /**
353      * Returns the String used to represent infinity. Almost always left
354      * unchanged.
355      * @return the Infinity string
356      * @stable ICU 2.0
357      */
358      //Bug 4194173 [Richard/GCL]
359
360     public String getInfinity() {
361         return infinity;
362     }
363
364     /**
365      * Sets the String used to represent infinity. Almost always left
366      * unchanged.
367      * @param infinity the Infinity String
368      * @stable ICU 2.0
369      */
370     public void setInfinity(String infinity) {
371         this.infinity = infinity;
372     }
373
374     /**
375      * Returns the String used to represent NaN. Almost always left
376      * unchanged.
377      * @return the NaN String
378      * @stable ICU 2.0
379      */
380      //Bug 4194173 [Richard/GCL]
381     public String getNaN() {
382         return NaN;
383     }
384
385     /**
386      * Sets the String used to represent NaN. Almost always left
387      * unchanged.
388      * @param NaN the NaN String
389      * @stable ICU 2.0
390      */
391     public void setNaN(String NaN) {
392         this.NaN = NaN;
393     }
394
395     /**
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
400      * @stable ICU 2.0
401      */
402     public char getMinusSign() {
403         return minusSign;
404     }
405
406     /**
407      * Returns the string used to represent minus sign.
408      * @return the minus sign string
409      * @internal
410      * @deprecated This API is ICU internal only.
411      */
412     public String getMinusString() {
413         return minusString;
414     }
415
416     /**
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
421      * @stable ICU 2.0
422      */
423     public void setMinusSign(char minusSign) {
424         this.minusSign = minusSign;
425         // Also updates minusString
426         char[] minusArray = { minusSign };
427         minusString = new String(minusArray);
428     }
429
430     /**
431      * Returns the string denoting the local currency.
432      * @return the local currency String.
433      * @stable ICU 2.0
434      */
435     public String getCurrencySymbol() {
436         return currencySymbol;
437     }
438
439     /**
440      * Sets the string denoting the local currency.
441      * @param currency the local currency String.
442      * @stable ICU 2.0
443      */
444     public void setCurrencySymbol(String currency) {
445         currencySymbol = currency;
446     }
447
448     /**
449      * Returns the international string denoting the local currency.
450      * @return the international string denoting the local currency
451      * @stable ICU 2.0
452      */
453     public String getInternationalCurrencySymbol() {
454         return intlCurrencySymbol;
455     }
456
457     /**
458      * Sets the international string denoting the local currency.
459      * @param currency the international string denoting the local currency.
460      * @stable ICU 2.0
461      */
462     public void setInternationalCurrencySymbol(String currency) {
463         intlCurrencySymbol = currency;
464     }
465
466     /**
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
470      * @stable ICU 3.4
471      */
472     public Currency getCurrency() {
473         return currency;
474     }
475
476     /**
477      * Sets the currency.
478      *
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.
481      *
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.
485      *
486      * @param currency the new currency to be used
487      * @throws NullPointerException if <code>currency</code> is null
488      * @see #setCurrencySymbol
489      * @see #setInternationalCurrencySymbol
490      *
491      * @stable ICU 3.4
492      */
493     public void setCurrency(Currency currency) {
494         if (currency == null) {
495             throw new NullPointerException();
496         }
497         this.currency = currency;
498         intlCurrencySymbol = currency.getCurrencyCode();
499         currencySymbol = currency.getSymbol(requestedLocale);
500     }
501
502     /**
503      * Returns the monetary decimal separator.
504      * @return the monetary decimal separator character
505      * @stable ICU 2.0
506      */
507     public char getMonetaryDecimalSeparator() {
508         return monetarySeparator;
509     }
510
511     /**
512      * {@icu} Returns the monetary grouping separator.
513      * @return the monetary grouping separator character
514      * @stable ICU 3.6
515      */
516     public char getMonetaryGroupingSeparator() {
517         return monetaryGroupingSeparator;
518     }
519
520     /**
521      * Internal API for NumberFormat
522      * @return String currency pattern string
523      */
524     String getCurrencyPattern() {
525         return currencyPattern;
526     }
527
528     /**
529      * Sets the monetary decimal separator.
530      * @param sep the monetary decimal separator character
531      * @stable ICU 2.0
532      */
533     public void setMonetaryDecimalSeparator(char sep) {
534         monetarySeparator = sep;
535     }
536
537     /**
538      * Sets the monetary decimal separator.
539      * @param sep the monetary decimal separator character
540      * @stable ICU 3.6
541      */
542     public void setMonetaryGroupingSeparator(char sep) {
543         monetaryGroupingSeparator = sep;
544     }
545
546     /**
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
552      * @stable ICU 2.0
553      */
554     public String getExponentSeparator() {
555         return exponentSeparator;
556     }
557
558     /**
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
564      * @stable ICU 2.0
565      */
566     public void setExponentSeparator(String exp) {
567         exponentSeparator = exp;
568     }
569
570     /**
571      * {@icu} Returns the localized plus sign.
572      * @return the plus sign, used in localized patterns and formatted
573      * strings
574      * @see #setPlusSign
575      * @see #setMinusSign
576      * @see #getMinusSign
577      * @stable ICU 2.0
578      */
579     public char getPlusSign() {
580         return plusSign;
581     }
582
583     /**
584      * Returns the string used to represent plus sign.
585      * @return the plus sign string
586      * @internal
587      * @deprecated This API is ICU internal only.
588      */
589     public String getPlusString() {
590         return plusString;
591     }
592
593     /**
594      * {@icu} Sets the localized plus sign.
595      * @param plus the plus sign, used in localized patterns and formatted
596      * strings
597      * @see #getPlusSign
598      * @see #setMinusSign
599      * @see #getMinusSign
600      * @stable ICU 2.0
601      */
602     public void setPlusSign(char plus) {
603         plusSign = plus;
604         // Also updates plusString
605         char[] plusArray = { plusSign };
606         plusString = new String(plusArray);
607     }
608
609     /**
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
615      * @see #setPadEscape
616      * @see DecimalFormat#getFormatWidth
617      * @see DecimalFormat#getPadPosition
618      * @see DecimalFormat#getPadCharacter
619      * @stable ICU 2.0
620      */
621     public char getPadEscape() {
622         return padEscape;
623     }
624
625     /**
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.
630      * @see #getPadEscape
631      * @see DecimalFormat#setFormatWidth
632      * @see DecimalFormat#setPadPosition
633      * @see DecimalFormat#setPadCharacter
634      * @stable ICU 2.0
635      */
636     public void setPadEscape(char c) {
637         padEscape = c;
638     }
639
640     /**
641      * {@icu} Indicates the currency match pattern used in {@link #getPatternForCurrencySpacing}.
642      * @stable ICU 4.2
643      */
644     public static final int CURRENCY_SPC_CURRENCY_MATCH = 0;
645
646     /**
647      * {@icu} Indicates the surrounding match pattern used in {@link
648      * #getPatternForCurrencySpacing}.
649      * @stable ICU 4.2
650      */
651     public static final int CURRENCY_SPC_SURROUNDING_MATCH = 1;
652
653     /**
654      * {@icu} Indicates the insertion value used in {@link #getPatternForCurrencySpacing}.
655      * @stable ICU 4.4
656      */
657     public static final int CURRENCY_SPC_INSERT = 2;
658
659     private String[] currencySpcBeforeSym;
660     private String[] currencySpcAfterSym;
661
662     /**
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.
667      *
668      * <p>For more information, see <a href="http://www.unicode.org/reports/tr35/#Currencies"
669      * >UTS#35 section 5.10.2</a>.
670      *
671      * <p><strong>Note:</strong> ICU4J does not currently use this information.
672      *
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)
679      * @stable ICU 4.2
680      */
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);
685         }
686         if (beforeCurrency) {
687             return currencySpcBeforeSym[itemType];
688         }
689         return currencySpcAfterSym[itemType];
690     }
691
692     /**
693      * {@icu} Sets the indicated currency spacing pattern or value. See {@link
694      * #getPatternForCurrencySpacing} for more information.
695      *
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.
698      *
699      * <p><strong>Note:</strong> ICU4J does not currently use this information.
700      *
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)
707      * @stable ICU 4.2
708      */
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);
713         }
714         if (beforeCurrency) {
715             currencySpcBeforeSym[itemType] = pattern;
716         } else {
717             currencySpcAfterSym[itemType] = pattern;
718         }
719     }
720
721     /**
722      * Returns the locale for which this object was constructed.
723      * @return the locale for which this object was constructed
724      * @stable ICU 2.0
725      */
726     public Locale getLocale() {
727         return requestedLocale;
728     }
729
730     /**
731      * Returns the locale for which this object was constructed.
732      * @return the locale for which this object was constructed
733      * @stable ICU 3.2
734      */
735     public ULocale getULocale() {
736         return ulocale;
737     }
738
739     /**
740      * {@inheritDoc}
741      * @stable ICU 2.0
742      */
743     public Object clone() {
744         try {
745             return (DecimalFormatSymbols) super.clone();
746             // other fields are bit-copied
747         } catch (CloneNotSupportedException e) {
748             ///CLOVER:OFF
749             throw new IllegalStateException();
750             ///CLOVER:ON
751         }
752     }
753
754     /**
755      * {@inheritDoc}
756      * @stable ICU 2.0
757      */
758     public boolean equals(Object obj) {
759         if (!(obj instanceof DecimalFormatSymbols)) {
760             return false;
761         }
762         if (this == obj) {
763             return true;
764         }
765         DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
766         for (int i = 0; i <= CURRENCY_SPC_INSERT; i++) {
767             if (!currencySpcBeforeSym[i].equals(other.currencySpcBeforeSym[i])) {
768                 return false;
769             }
770             if (!currencySpcAfterSym[i].equals(other.currencySpcAfterSym[i])) {
771                 return false;
772             }
773         }
774         
775         if ( other.digits == null ) {
776             for (int i = 0 ; i < 10 ; i++) {
777                 if (digits[i] != other.zeroDigit + i) {
778                     return false;
779                 }
780             }
781         } else if (!Arrays.equals(digits,other.digits)) {
782                     return false;
783         }
784
785         return (
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);
804     }
805
806     /**
807      * {@inheritDoc}
808      * @stable ICU 2.0
809      */
810     public int hashCode() {
811             int result = digits[0];
812             result = result * 37 + groupingSeparator;
813             result = result * 37 + decimalSeparator;
814             return result;
815     }
816
817     /**
818      * Check for bidi marks: LRM, RLM, ALM
819      */
820     private static boolean isBidiMark(char c) {
821         return (c=='\u200E' || c=='\u200F' || c=='\u061C');
822     }
823
824     /**
825      * Initializes the symbols from the LocaleElements resource bundle.
826      * Note: The organization of LocaleElements badly needs to be
827      * cleaned up.
828      */
829     private void initialize( ULocale locale ) {
830         this.requestedLocale = locale.toLocale();
831         this.ulocale = locale;
832
833         String nsName;
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();
851         } else {
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
863         }
864
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++ ) {
879                 try {
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.
883                         try {
884                             symbolsArray[i] = rb.getStringWithFallback(latnKey+symbolKeys[i]);
885                         } catch (MissingResourceException ex1) {
886                             symbolsArray[i] = fallbackElements[i];
887                         }
888                     } else {
889                         symbolsArray[i] = fallbackElements[i];
890                     }
891                 }
892             }
893
894             data[0] = symbolsArray;
895             /* update cache */
896             cachedLocaleData.put(locale, data);
897         }
898         numberElements = data[0];
899
900         ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
901             getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
902
903         // TODO: Determine actual and valid locale correctly.
904         ULocale uloc = r.getULocale();
905         setLocale(uloc, uloc);
906
907
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];
920
921         if ( numberElements[10] != null) {
922             monetarySeparator = numberElements[10].charAt(0);
923         } else {
924             monetarySeparator = decimalSeparator;
925         }
926         
927         if ( numberElements[11] != null) {
928             monetaryGroupingSeparator = numberElements[11].charAt(0);
929         } else {
930             monetaryGroupingSeparator = groupingSeparator;
931         }
932         
933         digit = DecimalFormat.PATTERN_DIGIT;  // Localized pattern character no longer in CLDR
934         padEscape = DecimalFormat.PATTERN_PAD_ESCAPE;
935         sigDigit  = DecimalFormat.PATTERN_SIGNIFICANT_DIGIT;
936
937
938         CurrencyDisplayInfo info = CurrencyData.provider.getInstance(locale, true);
939
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)
953                 : currname;
954             CurrencyFormatInfo fmtInfo = info.getFormatInfo(intlCurrencySymbol);
955             if (fmtInfo != null) {
956                 currencyPattern = fmtInfo.currencyPattern;
957                 monetarySeparator = fmtInfo.monetarySeparator;
958                 monetaryGroupingSeparator = fmtInfo.monetaryGroupingSeparator;
959             }
960         } else {
961             intlCurrencySymbol = "XXX";
962             currencySymbol = "\u00A4"; // 'OX' currency symbol
963         }
964
965
966         // Get currency spacing data.
967         currencySpcBeforeSym = new String[3];
968         currencySpcAfterSym = new String[3];
969         initSpacingInfo(info.getSpacingInfo());
970     }
971
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;
979     }
980
981     /**
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>
985      * to be 'E'.
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.
988      */
989     private void readObject(ObjectInputStream stream)
990         throws IOException, ClassNotFoundException {
991
992         // TODO: it looks to me {dlf} that the serialization code was never updated
993         // to handle the actual/valid ulocale fields.
994
995         stream.defaultReadObject();
996         ///CLOVER:OFF
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;
1000             // use defaults.
1001             monetarySeparator = decimalSeparator;
1002             exponential = 'E';
1003         }
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
1011             // symbol is there.
1012         }
1013         ///CLOVER:ON
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();
1021         }
1022         if (serialVersionOnStream < 4) {
1023             // use same default behavior as for versions with no Locale
1024             ulocale = ULocale.forLocale(requestedLocale);
1025         }
1026         if (serialVersionOnStream < 5) {
1027             // use the same one for groupingSeparator
1028             monetaryGroupingSeparator = groupingSeparator;
1029         }
1030         if (serialVersionOnStream < 6) {
1031             // Set null to CurrencySpacing related fields.
1032             if (currencySpcBeforeSym == null) {
1033                 currencySpcBeforeSym = new String[CURRENCY_SPC_INSERT+1];
1034             }
1035             if (currencySpcAfterSym == null) {
1036                 currencySpcAfterSym = new String[CURRENCY_SPC_INSERT+1];
1037             }
1038             initSpacingInfo(CurrencyData.CurrencySpacingInfo.DEFAULT);
1039         }
1040         if (serialVersionOnStream < 7) {
1041             // Set minusString,plusString from minusSign,plusSign
1042             if (minusString == null) {
1043                 char[] minusArray = { minusSign };
1044                 minusString = new String(minusArray);
1045             }
1046             if (plusString == null) {
1047                 char[] plusArray = { plusSign };
1048                 plusString = new String(plusArray);
1049             }
1050         }
1051         serialVersionOnStream = currentSerialVersion;
1052
1053     // recreate
1054     currency = Currency.getInstance(intlCurrencySymbol);
1055     }
1056
1057     /**
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.
1060      *
1061      * @serial
1062      * @see #getZeroDigit
1063      */
1064     private  char    zeroDigit;
1065     
1066     /**
1067      * Array of characters used for the digits 0-9 in order.
1068      *
1069      */   
1070     private  char    digits[];
1071
1072     /**
1073      * Character used for thousands separator.
1074      *
1075      * @serial
1076      * @see #getGroupingSeparator
1077      */
1078     private  char    groupingSeparator;
1079
1080     /**
1081      * Character used for decimal sign.
1082      *
1083      * @serial
1084      * @see #getDecimalSeparator
1085      */
1086     private  char    decimalSeparator;
1087
1088     /**
1089      * Character used for mille percent sign.
1090      *
1091      * @serial
1092      * @see #getPerMill
1093      */
1094     private  char    perMill;
1095
1096     /**
1097      * Character used for percent sign.
1098      * @serial
1099      * @see #getPercent
1100      */
1101     private  char    percent;
1102
1103     /**
1104      * Character used for a digit in a pattern.
1105      *
1106      * @serial
1107      * @see #getDigit
1108      */
1109     private  char    digit;
1110
1111     /**
1112      * Character used for a significant digit in a pattern.
1113      *
1114      * @serial
1115      * @see #getSignificantDigit
1116      */
1117     private  char    sigDigit;
1118
1119     /**
1120      * Character used to separate positive and negative subpatterns
1121      * in a pattern.
1122      *
1123      * @serial
1124      * @see #getPatternSeparator
1125      */
1126     private  char    patternSeparator;
1127
1128     /**
1129      * Character used to represent infinity.
1130      * @serial
1131      * @see #getInfinity
1132      */
1133     private  String  infinity;
1134
1135     /**
1136      * Character used to represent NaN.
1137      * @serial
1138      * @see #getNaN
1139      */
1140     private  String  NaN;
1141
1142     /**
1143      * Character used to represent minus sign.
1144      * @serial
1145      * @see #getMinusSign
1146      */
1147     private  char    minusSign;
1148
1149     /**
1150      * String denoting the local currency, e.g. "$".
1151      * @serial
1152      * @see #getCurrencySymbol
1153      */
1154     private  String  currencySymbol;
1155
1156     /**
1157      * International string denoting the local currency, e.g. "USD".
1158      * @serial
1159      * @see #getInternationalCurrencySymbol
1160      */
1161     private  String  intlCurrencySymbol;
1162
1163     /**
1164      * The decimal separator used when formatting currency values.
1165      * @serial
1166      * @see #getMonetaryDecimalSeparator
1167      */
1168     private  char    monetarySeparator; // Field new in JDK 1.1.6
1169
1170     /**
1171      * The decimal separator used when formatting currency values.
1172      * @serial
1173      * @see #getMonetaryGroupingSeparator
1174      */
1175     private  char    monetaryGroupingSeparator; // Field new in JDK 1.1.6
1176
1177     /**
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".
1180      * <p>
1181      * Note that this field has been superseded by <code>exponentSeparator</code>.
1182      * It is retained for backward compatibility.
1183      *
1184      * @serial
1185      */
1186     private  char    exponential;       // Field new in JDK 1.1.6
1187
1188     /**
1189      * The string used to separate the mantissa from the exponent.
1190      * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
1191      * <p>
1192      * Note that this supersedes the <code>exponential</code> field.
1193      *
1194      * @serial
1195      * @since AlphaWorks
1196      */
1197     private String exponentSeparator;
1198
1199     /**
1200      * The character used to indicate a padding character in a format,
1201      * e.g., '*' in a pattern such as "$*_#,##0.00".
1202      * @serial
1203      * @since AlphaWorks
1204      */
1205     private char padEscape;
1206
1207     /**
1208      * The character used to indicate a plus sign.
1209      * @serial
1210      * @since AlphaWorks
1211      */
1212     private char plusSign;
1213
1214     /**
1215      * The locale for which this object was constructed.  Set to the
1216      * default locale for objects resurrected from old streams.
1217      * @since ICU 2.2
1218      */
1219     private Locale requestedLocale;
1220
1221     /**
1222      * The requested ULocale.  We keep the old locale for serialization compatibility.
1223      * @since ICU 3.2
1224      */
1225     private ULocale ulocale;
1226
1227     /**
1228      * String versions of some number symbols.
1229      * @serial
1230      * @since ICU 52
1231      */
1232     private String minusString = null;
1233     private String plusString = null;
1234
1235     // Proclaim JDK 1.1 FCS compatibility
1236     private static final long serialVersionUID = 5772796243397350300L;
1237
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;
1250
1251     /**
1252      * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
1253      * Possible values are:
1254      * <ul>
1255      * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
1256      *
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,
1260      *      and plusSign.
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.
1267      * </ul>
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.
1271      *
1272      * @serial
1273      */
1274     private int serialVersionOnStream = currentSerialVersion;
1275
1276     /**
1277      * cache to hold the NumberElements of a Locale.
1278      */
1279     private static final ICUCache<ULocale, String[][]> cachedLocaleData =
1280         new SimpleCache<ULocale, String[][]>();
1281
1282     /**
1283      *
1284      */
1285     private String  currencyPattern = null;
1286
1287     // -------- BEGIN ULocale boilerplate --------
1288
1289     /**
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).
1297      *
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.
1310      */
1311     public final ULocale getLocale(ULocale.Type type) {
1312         return type == ULocale.ACTUAL_LOCALE ?
1313             this.actualLocale : this.validLocale;
1314     }
1315
1316     /**
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
1323      * this class.
1324      * @param valid the most specific locale containing any resource
1325      * data, or null
1326      * @param actual the locale containing data used to construct this
1327      * object, or null
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
1331      */
1332     final void setLocale(ULocale valid, ULocale actual) {
1333         // Change the following to an assertion later
1334         if ((valid == null) != (actual == null)) {
1335             ///CLOVER:OFF
1336             throw new IllegalArgumentException();
1337             ///CLOVER:ON
1338         }
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;
1343     }
1344
1345     /**
1346      * The most specific locale containing any resource data, or null.
1347      * @see com.ibm.icu.util.ULocale
1348      */
1349     private ULocale validLocale;
1350
1351     /**
1352      * The locale containing data used to construct this object, or
1353      * null.
1354      * @see com.ibm.icu.util.ULocale
1355      */
1356     private ULocale actualLocale;
1357
1358     // not serialized, reconstructed from intlCurrencyCode
1359     private transient Currency currency;
1360
1361     // -------- END ULocale boilerplate --------
1362 }