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