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 ('&#x2030;').
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