1/*
2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 *   The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 *   Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.IOException;
42import java.io.ObjectInputStream;
43import java.io.Serializable;
44import java.text.spi.DecimalFormatSymbolsProvider;
45import java.util.Currency;
46import java.util.Locale;
47import sun.util.locale.provider.LocaleProviderAdapter;
48import sun.util.locale.provider.LocaleServiceProviderPool;
49import sun.util.locale.provider.ResourceBundleBasedAdapter;
50
51/**
52 * This class represents the set of symbols (such as the decimal separator,
53 * the grouping separator, and so on) needed by <code>DecimalFormat</code>
54 * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
55 * <code>DecimalFormatSymbols</code> from its locale data.  If you need to change any
56 * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
57 * your <code>DecimalFormat</code> and modify it.
58 *
59 * @see          java.util.Locale
60 * @see          DecimalFormat
61 * @author       Mark Davis
62 * @author       Alan Liu
63 * @since 1.1
64 */
65
66public class DecimalFormatSymbols implements Cloneable, Serializable {
67
68    /**
69     * Create a DecimalFormatSymbols object for the default
70     * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
71     * This constructor can only construct instances for the locales
72     * supported by the Java runtime environment, not for those
73     * supported by installed
74     * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
75     * implementations. For full locale coverage, use the
76     * {@link #getInstance(Locale) getInstance} method.
77     * <p>This is equivalent to calling
78     * {@link #DecimalFormatSymbols(Locale)
79     *     DecimalFormatSymbols(Locale.getDefault(Locale.Category.FORMAT))}.
80     * @see java.util.Locale#getDefault(java.util.Locale.Category)
81     * @see java.util.Locale.Category#FORMAT
82     */
83    public DecimalFormatSymbols() {
84        initialize( Locale.getDefault(Locale.Category.FORMAT) );
85    }
86
87    /**
88     * Create a DecimalFormatSymbols object for the given locale.
89     * This constructor can only construct instances for the locales
90     * supported by the Java runtime environment, not for those
91     * supported by installed
92     * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
93     * implementations. For full locale coverage, use the
94     * {@link #getInstance(Locale) getInstance} method.
95     * If the specified locale contains the {@link java.util.Locale#UNICODE_LOCALE_EXTENSION}
96     * for the numbering system, the instance is initialized with the specified numbering
97     * system if the JRE implementation supports it. For example,
98     * <pre>
99     * NumberFormat.getNumberInstance(Locale.forLanguageTag("th-TH-u-nu-thai"))
100     * </pre>
101     * This may return a {@code NumberFormat} instance with the Thai numbering system,
102     * instead of the Latin numbering system.
103     *
104     * @param locale the desired locale
105     * @exception NullPointerException if <code>locale</code> is null
106     */
107    public DecimalFormatSymbols( Locale locale ) {
108        initialize( locale );
109    }
110
111    /**
112     * Returns an array of all locales for which the
113     * <code>getInstance</code> methods of this class can return
114     * localized instances.
115     * The returned array represents the union of locales supported by the Java
116     * runtime and by installed
117     * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
118     * implementations.  It must contain at least a <code>Locale</code>
119     * instance equal to {@link java.util.Locale#US Locale.US}.
120     *
121     * @return an array of locales for which localized
122     *         <code>DecimalFormatSymbols</code> instances are available.
123     * @since 1.6
124     */
125    public static Locale[] getAvailableLocales() {
126        LocaleServiceProviderPool pool =
127            LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
128        return pool.getAvailableLocales();
129    }
130
131    /**
132     * Gets the <code>DecimalFormatSymbols</code> instance for the default
133     * locale.  This method provides access to <code>DecimalFormatSymbols</code>
134     * instances for locales supported by the Java runtime itself as well
135     * as for those supported by installed
136     * {@link java.text.spi.DecimalFormatSymbolsProvider
137     * DecimalFormatSymbolsProvider} implementations.
138     * <p>This is equivalent to calling
139     * {@link #getInstance(Locale)
140     *     getInstance(Locale.getDefault(Locale.Category.FORMAT))}.
141     * @see java.util.Locale#getDefault(java.util.Locale.Category)
142     * @see java.util.Locale.Category#FORMAT
143     * @return a <code>DecimalFormatSymbols</code> instance.
144     * @since 1.6
145     */
146    public static final DecimalFormatSymbols getInstance() {
147        return getInstance(Locale.getDefault(Locale.Category.FORMAT));
148    }
149
150    /**
151     * Gets the <code>DecimalFormatSymbols</code> instance for the specified
152     * locale.  This method provides access to <code>DecimalFormatSymbols</code>
153     * instances for locales supported by the Java runtime itself as well
154     * as for those supported by installed
155     * {@link java.text.spi.DecimalFormatSymbolsProvider
156     * DecimalFormatSymbolsProvider} implementations.
157     * If the specified locale contains the {@link java.util.Locale#UNICODE_LOCALE_EXTENSION}
158     * for the numbering system, the instance is initialized with the specified numbering
159     * system if the JRE implementation supports it. For example,
160     * <pre>
161     * NumberFormat.getNumberInstance(Locale.forLanguageTag("th-TH-u-nu-thai"))
162     * </pre>
163     * This may return a {@code NumberFormat} instance with the Thai numbering system,
164     * instead of the Latin numbering system.
165     *
166     * @param locale the desired locale.
167     * @return a <code>DecimalFormatSymbols</code> instance.
168     * @exception NullPointerException if <code>locale</code> is null
169     * @since 1.6
170     */
171    public static final DecimalFormatSymbols getInstance(Locale locale) {
172        LocaleProviderAdapter adapter;
173        adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
174        DecimalFormatSymbolsProvider provider = adapter.getDecimalFormatSymbolsProvider();
175        DecimalFormatSymbols dfsyms = provider.getInstance(locale);
176        if (dfsyms == null) {
177            provider = LocaleProviderAdapter.forJRE().getDecimalFormatSymbolsProvider();
178            dfsyms = provider.getInstance(locale);
179        }
180        return dfsyms;
181    }
182
183    /**
184     * Gets the character used for zero. Different for Arabic, etc.
185     *
186     * @return the character used for zero
187     */
188    public char getZeroDigit() {
189        return zeroDigit;
190    }
191
192    /**
193     * Sets the character used for zero. Different for Arabic, etc.
194     *
195     * @param zeroDigit the character used for zero
196     */
197    public void setZeroDigit(char zeroDigit) {
198        this.zeroDigit = zeroDigit;
199    }
200
201    /**
202     * Gets the character used for thousands separator. Different for French, etc.
203     *
204     * @return the grouping separator
205     */
206    public char getGroupingSeparator() {
207        return groupingSeparator;
208    }
209
210    /**
211     * Sets the character used for thousands separator. Different for French, etc.
212     *
213     * @param groupingSeparator the grouping separator
214     */
215    public void setGroupingSeparator(char groupingSeparator) {
216        this.groupingSeparator = groupingSeparator;
217    }
218
219    /**
220     * Gets the character used for decimal sign. Different for French, etc.
221     *
222     * @return the character used for decimal sign
223     */
224    public char getDecimalSeparator() {
225        return decimalSeparator;
226    }
227
228    /**
229     * Sets the character used for decimal sign. Different for French, etc.
230     *
231     * @param decimalSeparator the character used for decimal sign
232     */
233    public void setDecimalSeparator(char decimalSeparator) {
234        this.decimalSeparator = decimalSeparator;
235    }
236
237    /**
238     * Gets the character used for per mille sign. Different for Arabic, etc.
239     *
240     * @return the character used for per mille sign
241     */
242    public char getPerMill() {
243        return perMill;
244    }
245
246    /**
247     * Sets the character used for per mille sign. Different for Arabic, etc.
248     *
249     * @param perMill the character used for per mille sign
250     */
251    public void setPerMill(char perMill) {
252        this.perMill = perMill;
253    }
254
255    /**
256     * Gets the character used for percent sign. Different for Arabic, etc.
257     *
258     * @return the character used for percent sign
259     */
260    public char getPercent() {
261        return percent;
262    }
263
264    /**
265     * Sets the character used for percent sign. Different for Arabic, etc.
266     *
267     * @param percent the character used for percent sign
268     */
269    public void setPercent(char percent) {
270        this.percent = percent;
271    }
272
273    /**
274     * Gets the character used for a digit in a pattern.
275     *
276     * @return the character used for a digit in a pattern
277     */
278    public char getDigit() {
279        return digit;
280    }
281
282    /**
283     * Sets the character used for a digit in a pattern.
284     *
285     * @param digit the character used for a digit in a pattern
286     */
287    public void setDigit(char digit) {
288        this.digit = digit;
289    }
290
291    /**
292     * Gets the character used to separate positive and negative subpatterns
293     * in a pattern.
294     *
295     * @return the pattern separator
296     */
297    public char getPatternSeparator() {
298        return patternSeparator;
299    }
300
301    /**
302     * Sets the character used to separate positive and negative subpatterns
303     * in a pattern.
304     *
305     * @param patternSeparator the pattern separator
306     */
307    public void setPatternSeparator(char patternSeparator) {
308        this.patternSeparator = patternSeparator;
309    }
310
311    /**
312     * Gets the string used to represent infinity. Almost always left
313     * unchanged.
314     *
315     * @return the string representing infinity
316     */
317    public String getInfinity() {
318        return infinity;
319    }
320
321    /**
322     * Sets the string used to represent infinity. Almost always left
323     * unchanged.
324     *
325     * @param infinity the string representing infinity
326     */
327    public void setInfinity(String infinity) {
328        this.infinity = infinity;
329    }
330
331    /**
332     * Gets the string used to represent "not a number". Almost always left
333     * unchanged.
334     *
335     * @return the string representing "not a number"
336     */
337    public String getNaN() {
338        return NaN;
339    }
340
341    /**
342     * Sets the string used to represent "not a number". Almost always left
343     * unchanged.
344     *
345     * @param NaN the string representing "not a number"
346     */
347    public void setNaN(String NaN) {
348        this.NaN = NaN;
349    }
350
351    /**
352     * Gets the character used to represent minus sign. If no explicit
353     * negative format is specified, one is formed by prefixing
354     * minusSign to the positive format.
355     *
356     * @return the character representing minus sign
357     */
358    public char getMinusSign() {
359        return minusSign;
360    }
361
362    /**
363     * Sets the character used to represent minus sign. If no explicit
364     * negative format is specified, one is formed by prefixing
365     * minusSign to the positive format.
366     *
367     * @param minusSign the character representing minus sign
368     */
369    public void setMinusSign(char minusSign) {
370        this.minusSign = minusSign;
371    }
372
373    /**
374     * Returns the currency symbol for the currency of these
375     * DecimalFormatSymbols in their locale.
376     *
377     * @return the currency symbol
378     * @since 1.2
379     */
380    public String getCurrencySymbol()
381    {
382        initializeCurrency(locale);
383        return currencySymbol;
384    }
385
386    /**
387     * Sets the currency symbol for the currency of these
388     * DecimalFormatSymbols in their locale.
389     *
390     * @param currency the currency symbol
391     * @since 1.2
392     */
393    public void setCurrencySymbol(String currency)
394    {
395        initializeCurrency(locale);
396        currencySymbol = currency;
397    }
398
399    /**
400     * Returns the ISO 4217 currency code of the currency of these
401     * DecimalFormatSymbols.
402     *
403     * @return the currency code
404     * @since 1.2
405     */
406    public String getInternationalCurrencySymbol()
407    {
408        initializeCurrency(locale);
409        return intlCurrencySymbol;
410    }
411
412    /**
413     * Sets the ISO 4217 currency code of the currency of these
414     * DecimalFormatSymbols.
415     * If the currency code is valid (as defined by
416     * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
417     * this also sets the currency attribute to the corresponding Currency
418     * instance and the currency symbol attribute to the currency's symbol
419     * in the DecimalFormatSymbols' locale. If the currency code is not valid,
420     * then the currency attribute is set to null and the currency symbol
421     * attribute is not modified.
422     *
423     * @param currencyCode the currency code
424     * @see #setCurrency
425     * @see #setCurrencySymbol
426     * @since 1.2
427     */
428    public void setInternationalCurrencySymbol(String currencyCode)
429    {
430        initializeCurrency(locale);
431        intlCurrencySymbol = currencyCode;
432        currency = null;
433        if (currencyCode != null) {
434            try {
435                currency = Currency.getInstance(currencyCode);
436                currencySymbol = currency.getSymbol();
437            } catch (IllegalArgumentException e) {
438            }
439        }
440    }
441
442    /**
443     * Gets the currency of these DecimalFormatSymbols. May be null if the
444     * currency symbol attribute was previously set to a value that's not
445     * a valid ISO 4217 currency code.
446     *
447     * @return the currency used, or null
448     * @since 1.4
449     */
450    public Currency getCurrency() {
451        initializeCurrency(locale);
452        return currency;
453    }
454
455    /**
456     * Sets the currency of these DecimalFormatSymbols.
457     * This also sets the currency symbol attribute to the currency's symbol
458     * in the DecimalFormatSymbols' locale, and the international currency
459     * symbol attribute to the currency's ISO 4217 currency code.
460     *
461     * @param currency the new currency to be used
462     * @exception NullPointerException if <code>currency</code> is null
463     * @since 1.4
464     * @see #setCurrencySymbol
465     * @see #setInternationalCurrencySymbol
466     */
467    public void setCurrency(Currency currency) {
468        if (currency == null) {
469            throw new NullPointerException();
470        }
471        initializeCurrency(locale);
472        this.currency = currency;
473        intlCurrencySymbol = currency.getCurrencyCode();
474        currencySymbol = currency.getSymbol(locale);
475    }
476
477
478    /**
479     * Returns the monetary decimal separator.
480     *
481     * @return the monetary decimal separator
482     * @since 1.2
483     */
484    public char getMonetaryDecimalSeparator()
485    {
486        return monetarySeparator;
487    }
488
489    /**
490     * Sets the monetary decimal separator.
491     *
492     * @param sep the monetary decimal separator
493     * @since 1.2
494     */
495    public void setMonetaryDecimalSeparator(char sep)
496    {
497        monetarySeparator = sep;
498    }
499
500    //------------------------------------------------------------
501    // BEGIN   Package Private methods ... to be made public later
502    //------------------------------------------------------------
503
504    /**
505     * Returns the character used to separate the mantissa from the exponent.
506     */
507    char getExponentialSymbol()
508    {
509        return exponential;
510    }
511
512    /**
513     * Returns the string used to separate the mantissa from the exponent.
514     * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
515     *
516     * @return the exponent separator string
517     * @see #setExponentSeparator(java.lang.String)
518     * @since 1.6
519     */
520    public String getExponentSeparator()
521    {
522        return exponentialSeparator;
523    }
524
525    /**
526     * Sets the character used to separate the mantissa from the exponent.
527     */
528    void setExponentialSymbol(char exp)
529    {
530        exponential = exp;
531    }
532
533    /**
534     * Sets the string used to separate the mantissa from the exponent.
535     * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
536     *
537     * @param exp the exponent separator string
538     * @exception NullPointerException if <code>exp</code> is null
539     * @see #getExponentSeparator()
540     * @since 1.6
541     */
542    public void setExponentSeparator(String exp)
543    {
544        if (exp == null) {
545            throw new NullPointerException();
546        }
547        exponentialSeparator = exp;
548    }
549
550
551    //------------------------------------------------------------
552    // END     Package Private methods ... to be made public later
553    //------------------------------------------------------------
554
555    /**
556     * Standard override.
557     */
558    @Override
559    public Object clone() {
560        try {
561            return (DecimalFormatSymbols)super.clone();
562            // other fields are bit-copied
563        } catch (CloneNotSupportedException e) {
564            throw new InternalError(e);
565        }
566    }
567
568    /**
569     * Override equals.
570     */
571    @Override
572    public boolean equals(Object obj) {
573        if (obj == null) return false;
574        if (this == obj) return true;
575        if (getClass() != obj.getClass()) return false;
576        DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
577        return (zeroDigit == other.zeroDigit &&
578        groupingSeparator == other.groupingSeparator &&
579        decimalSeparator == other.decimalSeparator &&
580        percent == other.percent &&
581        perMill == other.perMill &&
582        digit == other.digit &&
583        minusSign == other.minusSign &&
584        patternSeparator == other.patternSeparator &&
585        infinity.equals(other.infinity) &&
586        NaN.equals(other.NaN) &&
587        getCurrencySymbol().equals(other.getCurrencySymbol()) && // possible currency init occurs here
588        intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
589        currency == other.currency &&
590        monetarySeparator == other.monetarySeparator &&
591        exponentialSeparator.equals(other.exponentialSeparator) &&
592        locale.equals(other.locale));
593    }
594
595    /**
596     * Override hashCode.
597     */
598    @Override
599    public int hashCode() {
600            int result = zeroDigit;
601            result = result * 37 + groupingSeparator;
602            result = result * 37 + decimalSeparator;
603            return result;
604    }
605
606    /**
607     * Initializes the symbols from the FormatData resource bundle.
608     */
609    private void initialize( Locale locale ) {
610        this.locale = locale;
611
612        // get resource bundle data
613        LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
614        // Avoid potential recursions
615        if (!(adapter instanceof ResourceBundleBasedAdapter)) {
616            adapter = LocaleProviderAdapter.getResourceBundleBased();
617        }
618        Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData();
619        String[] numberElements = (String[]) data[0];
620
621        decimalSeparator = numberElements[0].charAt(0);
622        groupingSeparator = numberElements[1].charAt(0);
623        patternSeparator = numberElements[2].charAt(0);
624        percent = numberElements[3].charAt(0);
625        zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
626        digit = numberElements[5].charAt(0);
627        minusSign = numberElements[6].charAt(0);
628        exponential = numberElements[7].charAt(0);
629        exponentialSeparator = numberElements[7]; //string representation new since 1.6
630        perMill = numberElements[8].charAt(0);
631        infinity  = numberElements[9];
632        NaN = numberElements[10];
633
634        // maybe filled with previously cached values, or null.
635        intlCurrencySymbol = (String) data[1];
636        currencySymbol = (String) data[2];
637
638        // Currently the monetary decimal separator is the same as the
639        // standard decimal separator for all locales that we support.
640        // If that changes, add a new entry to NumberElements.
641        monetarySeparator = decimalSeparator;
642    }
643
644    /**
645     * Lazy initialization for currency related fields
646     */
647    private void initializeCurrency(Locale locale) {
648        if (currencyInitialized) {
649            return;
650        }
651
652        // Try to obtain the currency used in the locale's country.
653        // Check for empty country string separately because it's a valid
654        // country ID for Locale (and used for the C locale), but not a valid
655        // ISO 3166 country code, and exceptions are expensive.
656        if (locale.getCountry().length() > 0) {
657            try {
658                currency = Currency.getInstance(locale);
659            } catch (IllegalArgumentException e) {
660                // use default values below for compatibility
661            }
662        }
663
664        if (currency != null) {
665            // get resource bundle data
666            LocaleProviderAdapter adapter =
667                LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
668            // Avoid potential recursions
669            if (!(adapter instanceof ResourceBundleBasedAdapter)) {
670                adapter = LocaleProviderAdapter.getResourceBundleBased();
671            }
672            Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData();
673            intlCurrencySymbol = currency.getCurrencyCode();
674            if (data[1] != null && data[1] == intlCurrencySymbol) {
675                currencySymbol = (String) data[2];
676            } else {
677                currencySymbol = currency.getSymbol(locale);
678                data[1] = intlCurrencySymbol;
679                data[2] = currencySymbol;
680            }
681        } else {
682            // default values
683            intlCurrencySymbol = "XXX";
684            try {
685                currency = Currency.getInstance(intlCurrencySymbol);
686            } catch (IllegalArgumentException e) {
687            }
688            currencySymbol = "\u00A4";
689        }
690
691        currencyInitialized = true;
692    }
693
694    /**
695     * Reads the default serializable fields, provides default values for objects
696     * in older serial versions, and initializes non-serializable fields.
697     * If <code>serialVersionOnStream</code>
698     * is less than 1, initializes <code>monetarySeparator</code> to be
699     * the same as <code>decimalSeparator</code> and <code>exponential</code>
700     * to be 'E'.
701     * If <code>serialVersionOnStream</code> is less than 2,
702     * initializes <code>locale</code>to the root locale, and initializes
703     * If <code>serialVersionOnStream</code> is less than 3, it initializes
704     * <code>exponentialSeparator</code> using <code>exponential</code>.
705     * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
706     * default serialization will work properly if this object is streamed out again.
707     * Initializes the currency from the intlCurrencySymbol field.
708     *
709     * @since  1.1.6
710     */
711    private void readObject(ObjectInputStream stream)
712            throws IOException, ClassNotFoundException {
713        stream.defaultReadObject();
714        if (serialVersionOnStream < 1) {
715            // Didn't have monetarySeparator or exponential field;
716            // use defaults.
717            monetarySeparator = decimalSeparator;
718            exponential       = 'E';
719        }
720        if (serialVersionOnStream < 2) {
721            // didn't have locale; use root locale
722            locale = Locale.ROOT;
723        }
724        if (serialVersionOnStream < 3) {
725            // didn't have exponentialSeparator. Create one using exponential
726            exponentialSeparator = Character.toString(exponential);
727        }
728        serialVersionOnStream = currentSerialVersion;
729
730        if (intlCurrencySymbol != null) {
731            try {
732                 currency = Currency.getInstance(intlCurrencySymbol);
733            } catch (IllegalArgumentException e) {
734            }
735            currencyInitialized = true;
736        }
737    }
738
739    /**
740     * Character used for zero.
741     *
742     * @serial
743     * @see #getZeroDigit
744     */
745    private  char    zeroDigit;
746
747    /**
748     * Character used for thousands separator.
749     *
750     * @serial
751     * @see #getGroupingSeparator
752     */
753    private  char    groupingSeparator;
754
755    /**
756     * Character used for decimal sign.
757     *
758     * @serial
759     * @see #getDecimalSeparator
760     */
761    private  char    decimalSeparator;
762
763    /**
764     * Character used for per mille sign.
765     *
766     * @serial
767     * @see #getPerMill
768     */
769    private  char    perMill;
770
771    /**
772     * Character used for percent sign.
773     * @serial
774     * @see #getPercent
775     */
776    private  char    percent;
777
778    /**
779     * Character used for a digit in a pattern.
780     *
781     * @serial
782     * @see #getDigit
783     */
784    private  char    digit;
785
786    /**
787     * Character used to separate positive and negative subpatterns
788     * in a pattern.
789     *
790     * @serial
791     * @see #getPatternSeparator
792     */
793    private  char    patternSeparator;
794
795    /**
796     * String used to represent infinity.
797     * @serial
798     * @see #getInfinity
799     */
800    private  String  infinity;
801
802    /**
803     * String used to represent "not a number".
804     * @serial
805     * @see #getNaN
806     */
807    private  String  NaN;
808
809    /**
810     * Character used to represent minus sign.
811     * @serial
812     * @see #getMinusSign
813     */
814    private  char    minusSign;
815
816    /**
817     * String denoting the local currency, e.g. "$".
818     * @serial
819     * @see #getCurrencySymbol
820     */
821    private  String  currencySymbol;
822
823    /**
824     * ISO 4217 currency code denoting the local currency, e.g. "USD".
825     * @serial
826     * @see #getInternationalCurrencySymbol
827     */
828    private  String  intlCurrencySymbol;
829
830    /**
831     * The decimal separator used when formatting currency values.
832     * @serial
833     * @since  1.1.6
834     * @see #getMonetaryDecimalSeparator
835     */
836    private  char    monetarySeparator; // Field new in JDK 1.1.6
837
838    /**
839     * The character used to distinguish the exponent in a number formatted
840     * in exponential notation, e.g. 'E' for a number such as "1.23E45".
841     * <p>
842     * Note that the public API provides no way to set this field,
843     * even though it is supported by the implementation and the stream format.
844     * The intent is that this will be added to the API in the future.
845     *
846     * @serial
847     * @since  1.1.6
848     */
849    private  char    exponential;       // Field new in JDK 1.1.6
850
851    /**
852     * The string used to separate the mantissa from the exponent.
853     * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
854     * <p>
855     * If both <code>exponential</code> and <code>exponentialSeparator</code>
856     * exist, this <code>exponentialSeparator</code> has the precedence.
857     *
858     * @serial
859     * @since 1.6
860     */
861    private  String    exponentialSeparator;       // Field new in JDK 1.6
862
863    /**
864     * The locale of these currency format symbols.
865     *
866     * @serial
867     * @since 1.4
868     */
869    private Locale locale;
870
871    // currency; only the ISO code is serialized.
872    private transient Currency currency;
873    private transient volatile boolean currencyInitialized;
874
875    // Proclaim JDK 1.1 FCS compatibility
876    static final long serialVersionUID = 5772796243397350300L;
877
878    // The internal serial version which says which version was written
879    // - 0 (default) for version up to JDK 1.1.5
880    // - 1 for version from JDK 1.1.6, which includes two new fields:
881    //     monetarySeparator and exponential.
882    // - 2 for version from J2SE 1.4, which includes locale field.
883    // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
884    private static final int currentSerialVersion = 3;
885
886    /**
887     * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
888     * Possible values are:
889     * <ul>
890     * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
891     *
892     * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
893     *      two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
894     * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
895     *      new <code>locale</code> field.
896     * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
897     *      new <code>exponentialSeparator</code> field.
898     * </ul>
899     * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
900     * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
901     * is always written.
902     *
903     * @serial
904     * @since  1.1.6
905     */
906    private int serialVersionOnStream = currentSerialVersion;
907}
908