rt/emul/compact/src/main/java/java/text/DecimalFormatSymbols.java
author Jaroslav Tulach <jtulach@netbeans.org>
Thu, 03 Oct 2013 15:40:35 +0200
branchjdk7-b147
changeset 1334 588d5bf7a560
child 1339 8cc04f85a683
permissions -rw-r--r--
Set of JDK classes needed to run javac
     1 /*
     2  * Copyright (c) 1996, 2010, 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 
    39 package java.text;
    40 
    41 import java.io.IOException;
    42 import java.io.ObjectInputStream;
    43 import java.io.Serializable;
    44 import java.text.spi.DecimalFormatSymbolsProvider;
    45 import java.util.Currency;
    46 import java.util.Locale;
    47 import java.util.ResourceBundle;
    48 import java.util.concurrent.ConcurrentHashMap;
    49 
    50 import sun.util.LocaleServiceProviderPool;
    51 import sun.util.resources.LocaleData;
    52 
    53 /**
    54  * This class represents the set of symbols (such as the decimal separator,
    55  * the grouping separator, and so on) needed by <code>DecimalFormat</code>
    56  * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
    57  * <code>DecimalFormatSymbols</code> from its locale data.  If you need to change any
    58  * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
    59  * your <code>DecimalFormat</code> and modify it.
    60  *
    61  * @see          java.util.Locale
    62  * @see          DecimalFormat
    63  * @author       Mark Davis
    64  * @author       Alan Liu
    65  */
    66 
    67 public class DecimalFormatSymbols implements Cloneable, Serializable {
    68 
    69     /**
    70      * Create a DecimalFormatSymbols object for the default 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      */
    78     public DecimalFormatSymbols() {
    79         initialize( Locale.getDefault(Locale.Category.FORMAT) );
    80     }
    81 
    82     /**
    83      * Create a DecimalFormatSymbols object for the given locale.
    84      * This constructor can only construct instances for the locales
    85      * supported by the Java runtime environment, not for those
    86      * supported by installed
    87      * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
    88      * implementations. For full locale coverage, use the
    89      * {@link #getInstance(Locale) getInstance} method.
    90      *
    91      * @exception NullPointerException if <code>locale</code> is null
    92      */
    93     public DecimalFormatSymbols( Locale locale ) {
    94         initialize( locale );
    95     }
    96 
    97     /**
    98      * Returns an array of all locales for which the
    99      * <code>getInstance</code> methods of this class can return
   100      * localized instances.
   101      * The returned array represents the union of locales supported by the Java
   102      * runtime and by installed
   103      * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
   104      * implementations.  It must contain at least a <code>Locale</code>
   105      * instance equal to {@link java.util.Locale#US Locale.US}.
   106      *
   107      * @return An array of locales for which localized
   108      *         <code>DecimalFormatSymbols</code> instances are available.
   109      * @since 1.6
   110      */
   111     public static Locale[] getAvailableLocales() {
   112         LocaleServiceProviderPool pool =
   113             LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
   114         return pool.getAvailableLocales();
   115     }
   116 
   117     /**
   118      * Gets the <code>DecimalFormatSymbols</code> instance for the default
   119      * locale.  This method provides access to <code>DecimalFormatSymbols</code>
   120      * instances for locales supported by the Java runtime itself as well
   121      * as for those supported by installed
   122      * {@link java.text.spi.DecimalFormatSymbolsProvider
   123      * DecimalFormatSymbolsProvider} implementations.
   124      * @return a <code>DecimalFormatSymbols</code> instance.
   125      * @since 1.6
   126      */
   127     public static final DecimalFormatSymbols getInstance() {
   128         return getInstance(Locale.getDefault(Locale.Category.FORMAT));
   129     }
   130 
   131     /**
   132      * Gets the <code>DecimalFormatSymbols</code> instance for the specified
   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      * @param locale the desired locale.
   139      * @return a <code>DecimalFormatSymbols</code> instance.
   140      * @exception NullPointerException if <code>locale</code> is null
   141      * @since 1.6
   142      */
   143     public static final DecimalFormatSymbols getInstance(Locale locale) {
   144 
   145         // Check whether a provider can provide an implementation that's closer
   146         // to the requested locale than what the Java runtime itself can provide.
   147         LocaleServiceProviderPool pool =
   148             LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
   149         if (pool.hasProviders()) {
   150             DecimalFormatSymbols providersInstance = pool.getLocalizedObject(
   151                                 DecimalFormatSymbolsGetter.INSTANCE, locale);
   152             if (providersInstance != null) {
   153                 return providersInstance;
   154             }
   155         }
   156 
   157         return new DecimalFormatSymbols(locale);
   158     }
   159 
   160     /**
   161      * Gets the character used for zero. Different for Arabic, etc.
   162      */
   163     public char getZeroDigit() {
   164         return zeroDigit;
   165     }
   166 
   167     /**
   168      * Sets the character used for zero. Different for Arabic, etc.
   169      */
   170     public void setZeroDigit(char zeroDigit) {
   171         this.zeroDigit = zeroDigit;
   172     }
   173 
   174     /**
   175      * Gets the character used for thousands separator. Different for French, etc.
   176      */
   177     public char getGroupingSeparator() {
   178         return groupingSeparator;
   179     }
   180 
   181     /**
   182      * Sets the character used for thousands separator. Different for French, etc.
   183      */
   184     public void setGroupingSeparator(char groupingSeparator) {
   185         this.groupingSeparator = groupingSeparator;
   186     }
   187 
   188     /**
   189      * Gets the character used for decimal sign. Different for French, etc.
   190      */
   191     public char getDecimalSeparator() {
   192         return decimalSeparator;
   193     }
   194 
   195     /**
   196      * Sets the character used for decimal sign. Different for French, etc.
   197      */
   198     public void setDecimalSeparator(char decimalSeparator) {
   199         this.decimalSeparator = decimalSeparator;
   200     }
   201 
   202     /**
   203      * Gets the character used for per mille sign. Different for Arabic, etc.
   204      */
   205     public char getPerMill() {
   206         return perMill;
   207     }
   208 
   209     /**
   210      * Sets the character used for per mille sign. Different for Arabic, etc.
   211      */
   212     public void setPerMill(char perMill) {
   213         this.perMill = perMill;
   214     }
   215 
   216     /**
   217      * Gets the character used for percent sign. Different for Arabic, etc.
   218      */
   219     public char getPercent() {
   220         return percent;
   221     }
   222 
   223     /**
   224      * Sets the character used for percent sign. Different for Arabic, etc.
   225      */
   226     public void setPercent(char percent) {
   227         this.percent = percent;
   228     }
   229 
   230     /**
   231      * Gets the character used for a digit in a pattern.
   232      */
   233     public char getDigit() {
   234         return digit;
   235     }
   236 
   237     /**
   238      * Sets the character used for a digit in a pattern.
   239      */
   240     public void setDigit(char digit) {
   241         this.digit = digit;
   242     }
   243 
   244     /**
   245      * Gets the character used to separate positive and negative subpatterns
   246      * in a pattern.
   247      */
   248     public char getPatternSeparator() {
   249         return patternSeparator;
   250     }
   251 
   252     /**
   253      * Sets the character used to separate positive and negative subpatterns
   254      * in a pattern.
   255      */
   256     public void setPatternSeparator(char patternSeparator) {
   257         this.patternSeparator = patternSeparator;
   258     }
   259 
   260     /**
   261      * Gets the string used to represent infinity. Almost always left
   262      * unchanged.
   263      */
   264     public String getInfinity() {
   265         return infinity;
   266     }
   267 
   268     /**
   269      * Sets the string used to represent infinity. Almost always left
   270      * unchanged.
   271      */
   272     public void setInfinity(String infinity) {
   273         this.infinity = infinity;
   274     }
   275 
   276     /**
   277      * Gets the string used to represent "not a number". Almost always left
   278      * unchanged.
   279      */
   280     public String getNaN() {
   281         return NaN;
   282     }
   283 
   284     /**
   285      * Sets the string used to represent "not a number". Almost always left
   286      * unchanged.
   287      */
   288     public void setNaN(String NaN) {
   289         this.NaN = NaN;
   290     }
   291 
   292     /**
   293      * Gets the character used to represent minus sign. If no explicit
   294      * negative format is specified, one is formed by prefixing
   295      * minusSign to the positive format.
   296      */
   297     public char getMinusSign() {
   298         return minusSign;
   299     }
   300 
   301     /**
   302      * Sets the character used to represent minus sign. If no explicit
   303      * negative format is specified, one is formed by prefixing
   304      * minusSign to the positive format.
   305      */
   306     public void setMinusSign(char minusSign) {
   307         this.minusSign = minusSign;
   308     }
   309 
   310     /**
   311      * Returns the currency symbol for the currency of these
   312      * DecimalFormatSymbols in their locale.
   313      * @since 1.2
   314      */
   315     public String getCurrencySymbol()
   316     {
   317         return currencySymbol;
   318     }
   319 
   320     /**
   321      * Sets the currency symbol for the currency of these
   322      * DecimalFormatSymbols in their locale.
   323      * @since 1.2
   324      */
   325     public void setCurrencySymbol(String currency)
   326     {
   327         currencySymbol = currency;
   328     }
   329 
   330     /**
   331      * Returns the ISO 4217 currency code of the currency of these
   332      * DecimalFormatSymbols.
   333      * @since 1.2
   334      */
   335     public String getInternationalCurrencySymbol()
   336     {
   337         return intlCurrencySymbol;
   338     }
   339 
   340     /**
   341      * Sets the ISO 4217 currency code of the currency of these
   342      * DecimalFormatSymbols.
   343      * If the currency code is valid (as defined by
   344      * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
   345      * this also sets the currency attribute to the corresponding Currency
   346      * instance and the currency symbol attribute to the currency's symbol
   347      * in the DecimalFormatSymbols' locale. If the currency code is not valid,
   348      * then the currency attribute is set to null and the currency symbol
   349      * attribute is not modified.
   350      *
   351      * @see #setCurrency
   352      * @see #setCurrencySymbol
   353      * @since 1.2
   354      */
   355     public void setInternationalCurrencySymbol(String currencyCode)
   356     {
   357         intlCurrencySymbol = currencyCode;
   358         currency = null;
   359         if (currencyCode != null) {
   360             try {
   361                 currency = Currency.getInstance(currencyCode);
   362                 currencySymbol = currency.getSymbol();
   363             } catch (IllegalArgumentException e) {
   364             }
   365         }
   366     }
   367 
   368     /**
   369      * Gets the currency of these DecimalFormatSymbols. May be null if the
   370      * currency symbol attribute was previously set to a value that's not
   371      * a valid ISO 4217 currency code.
   372      *
   373      * @return the currency used, or null
   374      * @since 1.4
   375      */
   376     public Currency getCurrency() {
   377         return currency;
   378     }
   379 
   380     /**
   381      * Sets the currency of these DecimalFormatSymbols.
   382      * This also sets the currency symbol attribute to the currency's symbol
   383      * in the DecimalFormatSymbols' locale, and the international currency
   384      * symbol attribute to the currency's ISO 4217 currency code.
   385      *
   386      * @param currency the new currency to be used
   387      * @exception NullPointerException if <code>currency</code> is null
   388      * @since 1.4
   389      * @see #setCurrencySymbol
   390      * @see #setInternationalCurrencySymbol
   391      */
   392     public void setCurrency(Currency currency) {
   393         if (currency == null) {
   394             throw new NullPointerException();
   395         }
   396         this.currency = currency;
   397         intlCurrencySymbol = currency.getCurrencyCode();
   398         currencySymbol = currency.getSymbol(locale);
   399     }
   400 
   401 
   402     /**
   403      * Returns the monetary decimal separator.
   404      * @since 1.2
   405      */
   406     public char getMonetaryDecimalSeparator()
   407     {
   408         return monetarySeparator;
   409     }
   410 
   411     /**
   412      * Sets the monetary decimal separator.
   413      * @since 1.2
   414      */
   415     public void setMonetaryDecimalSeparator(char sep)
   416     {
   417         monetarySeparator = sep;
   418     }
   419 
   420     //------------------------------------------------------------
   421     // BEGIN   Package Private methods ... to be made public later
   422     //------------------------------------------------------------
   423 
   424     /**
   425      * Returns the character used to separate the mantissa from the exponent.
   426      */
   427     char getExponentialSymbol()
   428     {
   429         return exponential;
   430     }
   431   /**
   432    * Returns the string used to separate the mantissa from the exponent.
   433    * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
   434    *
   435    * @return the exponent separator string
   436    * @see #setExponentSeparator(java.lang.String)
   437    * @since 1.6
   438    */
   439     public String getExponentSeparator()
   440     {
   441         return exponentialSeparator;
   442     }
   443 
   444     /**
   445      * Sets the character used to separate the mantissa from the exponent.
   446      */
   447     void setExponentialSymbol(char exp)
   448     {
   449         exponential = exp;
   450     }
   451 
   452   /**
   453    * Sets the string used to separate the mantissa from the exponent.
   454    * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
   455    *
   456    * @param exp the exponent separator string
   457    * @exception NullPointerException if <code>exp</code> is null
   458    * @see #getExponentSeparator()
   459    * @since 1.6
   460    */
   461     public void setExponentSeparator(String exp)
   462     {
   463         if (exp == null) {
   464             throw new NullPointerException();
   465         }
   466         exponentialSeparator = exp;
   467      }
   468 
   469 
   470     //------------------------------------------------------------
   471     // END     Package Private methods ... to be made public later
   472     //------------------------------------------------------------
   473 
   474     /**
   475      * Standard override.
   476      */
   477     public Object clone() {
   478         try {
   479             return (DecimalFormatSymbols)super.clone();
   480             // other fields are bit-copied
   481         } catch (CloneNotSupportedException e) {
   482             throw new InternalError();
   483         }
   484     }
   485 
   486     /**
   487      * Override equals.
   488      */
   489     public boolean equals(Object obj) {
   490         if (obj == null) return false;
   491         if (this == obj) return true;
   492         if (getClass() != obj.getClass()) return false;
   493         DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
   494         return (zeroDigit == other.zeroDigit &&
   495         groupingSeparator == other.groupingSeparator &&
   496         decimalSeparator == other.decimalSeparator &&
   497         percent == other.percent &&
   498         perMill == other.perMill &&
   499         digit == other.digit &&
   500         minusSign == other.minusSign &&
   501         patternSeparator == other.patternSeparator &&
   502         infinity.equals(other.infinity) &&
   503         NaN.equals(other.NaN) &&
   504         currencySymbol.equals(other.currencySymbol) &&
   505         intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
   506         currency == other.currency &&
   507         monetarySeparator == other.monetarySeparator &&
   508         exponentialSeparator.equals(other.exponentialSeparator) &&
   509         locale.equals(other.locale));
   510     }
   511 
   512     /**
   513      * Override hashCode.
   514      */
   515     public int hashCode() {
   516             int result = zeroDigit;
   517             result = result * 37 + groupingSeparator;
   518             result = result * 37 + decimalSeparator;
   519             return result;
   520     }
   521 
   522     /**
   523      * Initializes the symbols from the FormatData resource bundle.
   524      */
   525     private void initialize( Locale locale ) {
   526         this.locale = locale;
   527 
   528         // get resource bundle data - try the cache first
   529         boolean needCacheUpdate = false;
   530         Object[] data = cachedLocaleData.get(locale);
   531         if (data == null) {  /* cache miss */
   532             // When numbering system is thai (Locale's extension contains u-nu-thai),
   533             // we read the data from th_TH_TH.
   534             Locale lookupLocale = locale;
   535             String numberType = locale.getUnicodeLocaleType("nu");
   536             if (numberType != null && numberType.equals("thai")) {
   537                 lookupLocale = new Locale("th", "TH", "TH");
   538             }
   539             data = new Object[3];
   540             ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
   541             data[0] = rb.getStringArray("NumberElements");
   542             needCacheUpdate = true;
   543         }
   544 
   545         String[] numberElements = (String[]) data[0];
   546 
   547         decimalSeparator = numberElements[0].charAt(0);
   548         groupingSeparator = numberElements[1].charAt(0);
   549         patternSeparator = numberElements[2].charAt(0);
   550         percent = numberElements[3].charAt(0);
   551         zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
   552         digit = numberElements[5].charAt(0);
   553         minusSign = numberElements[6].charAt(0);
   554         exponential = numberElements[7].charAt(0);
   555         exponentialSeparator = numberElements[7]; //string representation new since 1.6
   556         perMill = numberElements[8].charAt(0);
   557         infinity  = numberElements[9];
   558         NaN = numberElements[10];
   559 
   560         // Try to obtain the currency used in the locale's country.
   561         // Check for empty country string separately because it's a valid
   562         // country ID for Locale (and used for the C locale), but not a valid
   563         // ISO 3166 country code, and exceptions are expensive.
   564         if (!"".equals(locale.getCountry())) {
   565             try {
   566                 currency = Currency.getInstance(locale);
   567             } catch (IllegalArgumentException e) {
   568                 // use default values below for compatibility
   569             }
   570         }
   571         if (currency != null) {
   572             intlCurrencySymbol = currency.getCurrencyCode();
   573             if (data[1] != null && data[1] == intlCurrencySymbol) {
   574                 currencySymbol = (String) data[2];
   575             } else {
   576                 currencySymbol = currency.getSymbol(locale);
   577                 data[1] = intlCurrencySymbol;
   578                 data[2] = currencySymbol;
   579                 needCacheUpdate = true;
   580             }
   581         } else {
   582             // default values
   583             intlCurrencySymbol = "XXX";
   584             try {
   585                 currency = Currency.getInstance(intlCurrencySymbol);
   586             } catch (IllegalArgumentException e) {
   587             }
   588             currencySymbol = "\u00A4";
   589         }
   590         // Currently the monetary decimal separator is the same as the
   591         // standard decimal separator for all locales that we support.
   592         // If that changes, add a new entry to NumberElements.
   593         monetarySeparator = decimalSeparator;
   594 
   595         if (needCacheUpdate) {
   596             cachedLocaleData.putIfAbsent(locale, data);
   597         }
   598     }
   599 
   600     /**
   601      * Reads the default serializable fields, provides default values for objects
   602      * in older serial versions, and initializes non-serializable fields.
   603      * If <code>serialVersionOnStream</code>
   604      * is less than 1, initializes <code>monetarySeparator</code> to be
   605      * the same as <code>decimalSeparator</code> and <code>exponential</code>
   606      * to be 'E'.
   607      * If <code>serialVersionOnStream</code> is less than 2,
   608      * initializes <code>locale</code>to the root locale, and initializes
   609      * If <code>serialVersionOnStream</code> is less than 3, it initializes
   610      * <code>exponentialSeparator</code> using <code>exponential</code>.
   611      * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
   612      * default serialization will work properly if this object is streamed out again.
   613      * Initializes the currency from the intlCurrencySymbol field.
   614      *
   615      * @since JDK 1.1.6
   616      */
   617     private void readObject(ObjectInputStream stream)
   618             throws IOException, ClassNotFoundException {
   619         stream.defaultReadObject();
   620         if (serialVersionOnStream < 1) {
   621             // Didn't have monetarySeparator or exponential field;
   622             // use defaults.
   623             monetarySeparator = decimalSeparator;
   624             exponential       = 'E';
   625         }
   626         if (serialVersionOnStream < 2) {
   627             // didn't have locale; use root locale
   628             locale = Locale.ROOT;
   629         }
   630         if (serialVersionOnStream < 3) {
   631             // didn't have exponentialSeparator. Create one using exponential
   632             exponentialSeparator = Character.toString(exponential);
   633         }
   634         serialVersionOnStream = currentSerialVersion;
   635 
   636         if (intlCurrencySymbol != null) {
   637             try {
   638                  currency = Currency.getInstance(intlCurrencySymbol);
   639             } catch (IllegalArgumentException e) {
   640             }
   641         }
   642     }
   643 
   644     /**
   645      * Character used for zero.
   646      *
   647      * @serial
   648      * @see #getZeroDigit
   649      */
   650     private  char    zeroDigit;
   651 
   652     /**
   653      * Character used for thousands separator.
   654      *
   655      * @serial
   656      * @see #getGroupingSeparator
   657      */
   658     private  char    groupingSeparator;
   659 
   660     /**
   661      * Character used for decimal sign.
   662      *
   663      * @serial
   664      * @see #getDecimalSeparator
   665      */
   666     private  char    decimalSeparator;
   667 
   668     /**
   669      * Character used for per mille sign.
   670      *
   671      * @serial
   672      * @see #getPerMill
   673      */
   674     private  char    perMill;
   675 
   676     /**
   677      * Character used for percent sign.
   678      * @serial
   679      * @see #getPercent
   680      */
   681     private  char    percent;
   682 
   683     /**
   684      * Character used for a digit in a pattern.
   685      *
   686      * @serial
   687      * @see #getDigit
   688      */
   689     private  char    digit;
   690 
   691     /**
   692      * Character used to separate positive and negative subpatterns
   693      * in a pattern.
   694      *
   695      * @serial
   696      * @see #getPatternSeparator
   697      */
   698     private  char    patternSeparator;
   699 
   700     /**
   701      * String used to represent infinity.
   702      * @serial
   703      * @see #getInfinity
   704      */
   705     private  String  infinity;
   706 
   707     /**
   708      * String used to represent "not a number".
   709      * @serial
   710      * @see #getNaN
   711      */
   712     private  String  NaN;
   713 
   714     /**
   715      * Character used to represent minus sign.
   716      * @serial
   717      * @see #getMinusSign
   718      */
   719     private  char    minusSign;
   720 
   721     /**
   722      * String denoting the local currency, e.g. "$".
   723      * @serial
   724      * @see #getCurrencySymbol
   725      */
   726     private  String  currencySymbol;
   727 
   728     /**
   729      * ISO 4217 currency code denoting the local currency, e.g. "USD".
   730      * @serial
   731      * @see #getInternationalCurrencySymbol
   732      */
   733     private  String  intlCurrencySymbol;
   734 
   735     /**
   736      * The decimal separator used when formatting currency values.
   737      * @serial
   738      * @since JDK 1.1.6
   739      * @see #getMonetaryDecimalSeparator
   740      */
   741     private  char    monetarySeparator; // Field new in JDK 1.1.6
   742 
   743     /**
   744      * The character used to distinguish the exponent in a number formatted
   745      * in exponential notation, e.g. 'E' for a number such as "1.23E45".
   746      * <p>
   747      * Note that the public API provides no way to set this field,
   748      * even though it is supported by the implementation and the stream format.
   749      * The intent is that this will be added to the API in the future.
   750      *
   751      * @serial
   752      * @since JDK 1.1.6
   753      */
   754     private  char    exponential;       // Field new in JDK 1.1.6
   755 
   756   /**
   757    * The string used to separate the mantissa from the exponent.
   758    * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
   759    * <p>
   760    * If both <code>exponential</code> and <code>exponentialSeparator</code>
   761    * exist, this <code>exponentialSeparator</code> has the precedence.
   762    *
   763    * @serial
   764    * @since 1.6
   765    */
   766     private  String    exponentialSeparator;       // Field new in JDK 1.6
   767 
   768     /**
   769      * The locale of these currency format symbols.
   770      *
   771      * @serial
   772      * @since 1.4
   773      */
   774     private Locale locale;
   775 
   776     // currency; only the ISO code is serialized.
   777     private transient Currency currency;
   778 
   779     // Proclaim JDK 1.1 FCS compatibility
   780     static final long serialVersionUID = 5772796243397350300L;
   781 
   782     // The internal serial version which says which version was written
   783     // - 0 (default) for version up to JDK 1.1.5
   784     // - 1 for version from JDK 1.1.6, which includes two new fields:
   785     //     monetarySeparator and exponential.
   786     // - 2 for version from J2SE 1.4, which includes locale field.
   787     // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
   788     private static final int currentSerialVersion = 3;
   789 
   790     /**
   791      * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
   792      * Possible values are:
   793      * <ul>
   794      * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
   795      *
   796      * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
   797      *      two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
   798      * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
   799      *      new <code>locale</code> field.
   800      * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
   801      *      new <code>exponentialSeparator</code> field.
   802      * </ul>
   803      * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
   804      * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
   805      * is always written.
   806      *
   807      * @serial
   808      * @since JDK 1.1.6
   809      */
   810     private int serialVersionOnStream = currentSerialVersion;
   811 
   812     /**
   813      * cache to hold the NumberElements and the Currency
   814      * of a Locale.
   815      */
   816     private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
   817 
   818     /**
   819      * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
   820      * implementation.
   821      */
   822     private static class DecimalFormatSymbolsGetter
   823         implements LocaleServiceProviderPool.LocalizedObjectGetter<DecimalFormatSymbolsProvider,
   824                                                                    DecimalFormatSymbols> {
   825         private static final DecimalFormatSymbolsGetter INSTANCE =
   826             new DecimalFormatSymbolsGetter();
   827 
   828         public DecimalFormatSymbols getObject(
   829                                 DecimalFormatSymbolsProvider decimalFormatSymbolsProvider,
   830                                 Locale locale,
   831                                 String key,
   832                                 Object... params) {
   833             assert params.length == 0;
   834             return decimalFormatSymbolsProvider.getInstance(locale);
   835         }
   836     }
   837 }