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