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