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