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