src/share/classes/java/util/Currency.java

Print this page
rev 5615 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o Jigsaw. by Naoto Sato and Masayoshi Okutsu)


  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 package java.util;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.DataInputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileReader;
  33 import java.io.IOException;
  34 import java.io.Serializable;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.concurrent.ConcurrentHashMap;
  38 import java.util.concurrent.ConcurrentMap;
  39 import java.util.logging.Level;
  40 import java.util.regex.Pattern;
  41 import java.util.regex.Matcher;
  42 import java.util.spi.CurrencyNameProvider;
  43 import java.util.spi.LocaleServiceProvider;
  44 import sun.util.LocaleServiceProviderPool;
  45 import sun.util.logging.PlatformLogger;
  46 import sun.util.resources.LocaleData;
  47 import sun.util.resources.OpenListResourceBundle;
  48 
  49 
  50 /**
  51  * Represents a currency. Currencies are identified by their ISO 4217 currency
  52  * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
  53  * ISO web site</a> for more information, including a table of
  54  * currency codes.
  55  * <p>
  56  * The class is designed so that there's never more than one
  57  * <code>Currency</code> instance for any given currency. Therefore, there's
  58  * no public constructor. You obtain a <code>Currency</code> instance using
  59  * the <code>getInstance</code> methods.
  60  * <p>
  61  * Users can supersede the Java runtime currency data by creating a properties
  62  * file named <code>&lt;JAVA_HOME&gt;/lib/currency.properties</code>.  The contents
  63  * of the properties file are key/value pairs of the ISO 3166 country codes
  64  * and the ISO 4217 currency data respectively.  The value part consists of
  65  * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
  66  * code, and a minor unit.  Those three ISO 4217 values are separated by commas.
  67  * The lines which start with '#'s are considered comment lines.  For example,


 174     private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
 175     // shift count for simple case country entry default currency digits
 176     private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
 177     // mask for special case country entries
 178     private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
 179     // mask for special case country index
 180     private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
 181     // delta from entry index component in main table to index into special case tables
 182     private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
 183     // mask for distinguishing simple and special case countries
 184     private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
 185     // mask for the numeric code of the currency
 186     private static final int NUMERIC_CODE_MASK = 0x0003FF00;
 187     // shift count for the numeric code of the currency
 188     private static final int NUMERIC_CODE_SHIFT = 8;
 189 
 190     // Currency data format version
 191     private static final int VALID_FORMAT_VERSION = 1;
 192 
 193     static {
 194         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 195             public Object run() {

 196                 String homeDir = System.getProperty("java.home");
 197                 try {
 198                     String dataFile = homeDir + File.separator +
 199                             "lib" + File.separator + "currency.data";
 200                     DataInputStream dis = new DataInputStream(
 201                         new BufferedInputStream(
 202                         new FileInputStream(dataFile)));
 203                     if (dis.readInt() != MAGIC_NUMBER) {
 204                         throw new InternalError("Currency data is possibly corrupted");
 205                     }
 206                     formatVersion = dis.readInt();
 207                     if (formatVersion != VALID_FORMAT_VERSION) {
 208                         throw new InternalError("Currency data format is incorrect");
 209                     }
 210                     dataVersion = dis.readInt();
 211                     mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
 212                     int scCount = dis.readInt();
 213                     scCutOverTimes = readLongArray(dis, scCount);
 214                     scOldCurrencies = readStringArray(dis, scCount);
 215                     scNewCurrencies = readStringArray(dis, scCount);
 216                     scOldCurrenciesDFD = readIntArray(dis, scCount);
 217                     scNewCurrenciesDFD = readIntArray(dis, scCount);
 218                     scOldCurrenciesNumericCode = readIntArray(dis, scCount);
 219                     scNewCurrenciesNumericCode = readIntArray(dis, scCount);
 220                     int ocCount = dis.readInt();
 221                     otherCurrencies = dis.readUTF();
 222                     otherCurrenciesDFD = readIntArray(dis, ocCount);
 223                     otherCurrenciesNumericCode = readIntArray(dis, ocCount);
 224                     dis.close();
 225                 } catch (IOException e) {
 226                     throw new InternalError(e);
 227                 }
 228 
 229                 // look for the properties file for overrides
 230                 try {
 231                     File propFile = new File(homeDir + File.separator +
 232                                              "lib" + File.separator +
 233                                              "currency.properties");
 234                     if (propFile.exists()) {
 235                         Properties props = new Properties();
 236                         try (FileReader fr = new FileReader(propFile)) {
 237                             props.load(fr);
 238                         }
 239                         Set<String> keys = props.stringPropertyNames();
 240                         Pattern propertiesPattern =
 241                             Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
 242                         for (String key : keys) {
 243                            replaceCurrencyData(propertiesPattern,
 244                                key.toUpperCase(Locale.ROOT),


 327             new Currency(currencyCode, defaultFractionDigits, numericCode);
 328         instance = instances.putIfAbsent(currencyCode, currencyVal);
 329         return (instance != null ? instance : currencyVal);
 330     }
 331 
 332     /**
 333      * Returns the <code>Currency</code> instance for the country of the
 334      * given locale. The language and variant components of the locale
 335      * are ignored. The result may vary over time, as countries change their
 336      * currencies. For example, for the original member countries of the
 337      * European Monetary Union, the method returns the old national currencies
 338      * until December 31, 2001, and the Euro from January 1, 2002, local time
 339      * of the respective countries.
 340      * <p>
 341      * The method returns <code>null</code> for territories that don't
 342      * have a currency, such as Antarctica.
 343      *
 344      * @param locale the locale for whose country a <code>Currency</code>
 345      * instance is needed
 346      * @return the <code>Currency</code> instance for the country of the given
 347      * locale, or null
 348      * @exception NullPointerException if <code>locale</code> or its country
 349      * code is null
 350      * @exception IllegalArgumentException if the country of the given locale
 351      * is not a supported ISO 3166 country code.
 352      */
 353     public static Currency getInstance(Locale locale) {
 354         String country = locale.getCountry();
 355         if (country == null) {
 356             throw new NullPointerException();
 357         }
 358 
 359         if (country.length() != 2) {
 360             throw new IllegalArgumentException();
 361         }
 362 
 363         char char1 = country.charAt(0);
 364         char char2 = country.charAt(1);
 365         int tableEntry = getMainTableEntry(char1, char2);
 366         if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
 367                     && tableEntry != INVALID_COUNTRY_ENTRY) {
 368             char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
 369             int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
 370             int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
 371             StringBuffer sb = new StringBuffer(country);
 372             sb.append(finalChar);
 373             return getInstance(sb.toString(), defaultFractionDigits, numericCode);
 374         } else {
 375             // special cases
 376             if (tableEntry == INVALID_COUNTRY_ENTRY) {
 377                 throw new IllegalArgumentException();
 378             }
 379             if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
 380                 return null;
 381             } else {
 382                 int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
 383                 if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
 384                     return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
 385                         scOldCurrenciesNumericCode[index]);
 386                 } else {
 387                     return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
 388                         scNewCurrenciesNumericCode[index]);
 389                 }
 390             }
 391         }


 453      * symbol can be determined, the ISO 4217 currency code is returned.
 454      *
 455      * @return the symbol of this currency for the default locale
 456      */
 457     public String getSymbol() {
 458         return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
 459     }
 460 
 461     /**
 462      * Gets the symbol of this currency for the specified locale.
 463      * For example, for the US Dollar, the symbol is "$" if the specified
 464      * locale is the US, while for other locales it may be "US$". If no
 465      * symbol can be determined, the ISO 4217 currency code is returned.
 466      *
 467      * @param locale the locale for which a display name for this currency is
 468      * needed
 469      * @return the symbol of this currency for the specified locale
 470      * @exception NullPointerException if <code>locale</code> is null
 471      */
 472     public String getSymbol(Locale locale) {
 473         try {
 474             // Check whether a provider can provide an implementation that's closer
 475             // to the requested locale than what the Java runtime itself can provide.
 476             LocaleServiceProviderPool pool =
 477                 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
 478 
 479             if (pool.hasProviders()) {
 480                 // Assuming that all the country locales include necessary currency
 481                 // symbols in the Java runtime's resources,  so there is no need to
 482                 // examine whether Java runtime's currency resource bundle is missing
 483                 // names.  Therefore, no resource bundle is provided for calling this
 484                 // method.
 485                 String symbol = pool.getLocalizedObject(
 486                                     CurrencyNameGetter.INSTANCE,
 487                                     locale, (OpenListResourceBundle)null,
 488                                     currencyCode, SYMBOL);
 489                 if (symbol != null) {
 490                     return symbol;
 491                 }
 492             }
 493 
 494             ResourceBundle bundle = LocaleData.getCurrencyNames(locale);
 495             return bundle.getString(currencyCode);
 496         } catch (MissingResourceException e) {
 497             // use currency code as symbol of last resort
 498             return currencyCode;
 499         }
 500     }
 501 
 502     /**
 503      * Gets the default number of fraction digits used with this currency.
 504      * For example, the default number of fraction digits for the Euro is 2,
 505      * while for the Japanese Yen it's 0.
 506      * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
 507      * -1 is returned.
 508      *
 509      * @return the default number of fraction digits used with this currency
 510      */
 511     public int getDefaultFractionDigits() {
 512         return defaultFractionDigits;
 513     }
 514 
 515     /**
 516      * Returns the ISO 4217 numeric code of this currency.
 517      *
 518      * @return the ISO 4217 numeric code of this currency
 519      * @since 1.7
 520      */


 529      *
 530      * @return the display name of this currency for the default locale
 531      * @since 1.7
 532      */
 533     public String getDisplayName() {
 534         return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
 535     }
 536 
 537     /**
 538      * Gets the name that is suitable for displaying this currency for
 539      * the specified locale.  If there is no suitable display name found
 540      * for the specified locale, the ISO 4217 currency code is returned.
 541      *
 542      * @param locale the locale for which a display name for this currency is
 543      * needed
 544      * @return the display name of this currency for the specified locale
 545      * @exception NullPointerException if <code>locale</code> is null
 546      * @since 1.7
 547      */
 548     public String getDisplayName(Locale locale) {
 549         try {
 550             OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
 551             String result = null;
 552             String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
 553 
 554             // Check whether a provider can provide an implementation that's closer
 555             // to the requested locale than what the Java runtime itself can provide.
 556             LocaleServiceProviderPool pool =
 557                 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
 558             if (pool.hasProviders()) {
 559                 result = pool.getLocalizedObject(
 560                                     CurrencyNameGetter.INSTANCE,
 561                                     locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
 562             }
 563 
 564             if (result == null) {
 565                 result = bundle.getString(bundleKey);
 566             }
 567 
 568             if (result != null) {
 569                 return result;
 570             }
 571         } catch (MissingResourceException e) {
 572             // fall through
 573         }
 574 
 575         // use currency code as symbol of last resort
 576         return currencyCode;
 577     }
 578 
 579     /**
 580      * Returns the ISO 4217 currency code of this currency.
 581      *
 582      * @return the ISO 4217 currency code of this currency
 583      */

 584     public String toString() {
 585         return currencyCode;
 586     }
 587 
 588     /**
 589      * Resolves instances being deserialized to a single instance per currency.
 590      */
 591     private Object readResolve() {
 592         return getInstance(currencyCode);
 593     }
 594 
 595     /**
 596      * Gets the main table entry for the country whose country code consists
 597      * of char1 and char2.
 598      */
 599     private static int getMainTableEntry(char char1, char char2) {
 600         if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
 601             throw new IllegalArgumentException();
 602         }
 603         return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];


 606     /**
 607      * Sets the main table entry for the country whose country code consists
 608      * of char1 and char2.
 609      */
 610     private static void setMainTableEntry(char char1, char char2, int entry) {
 611         if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
 612             throw new IllegalArgumentException();
 613         }
 614         mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
 615     }
 616 
 617     /**
 618      * Obtains a localized currency names from a CurrencyNameProvider
 619      * implementation.
 620      */
 621     private static class CurrencyNameGetter
 622         implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
 623                                                                    String> {
 624         private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
 625 

 626         public String getObject(CurrencyNameProvider currencyNameProvider,
 627                                 Locale locale,
 628                                 String key,
 629                                 Object... params) {
 630             assert params.length == 1;
 631             int type = (Integer)params[0];
 632 
 633             switch(type) {
 634             case SYMBOL:
 635                 return currencyNameProvider.getSymbol(key, locale);
 636             case DISPLAYNAME:
 637                 return currencyNameProvider.getDisplayName(key, locale);
 638             default:
 639                 assert false; // shouldn't happen
 640             }
 641 
 642             return null;
 643         }
 644     }
 645 




  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 package java.util;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.DataInputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileReader;
  33 import java.io.IOException;
  34 import java.io.Serializable;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.util.concurrent.ConcurrentHashMap;
  38 import java.util.concurrent.ConcurrentMap;

  39 import java.util.regex.Pattern;
  40 import java.util.regex.Matcher;
  41 import java.util.spi.CurrencyNameProvider;
  42 import sun.util.locale.provider.LocaleServiceProviderPool;

  43 import sun.util.logging.PlatformLogger;


  44 
  45 
  46 /**
  47  * Represents a currency. Currencies are identified by their ISO 4217 currency
  48  * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
  49  * ISO web site</a> for more information, including a table of
  50  * currency codes.
  51  * <p>
  52  * The class is designed so that there's never more than one
  53  * <code>Currency</code> instance for any given currency. Therefore, there's
  54  * no public constructor. You obtain a <code>Currency</code> instance using
  55  * the <code>getInstance</code> methods.
  56  * <p>
  57  * Users can supersede the Java runtime currency data by creating a properties
  58  * file named <code>&lt;JAVA_HOME&gt;/lib/currency.properties</code>.  The contents
  59  * of the properties file are key/value pairs of the ISO 3166 country codes
  60  * and the ISO 4217 currency data respectively.  The value part consists of
  61  * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
  62  * code, and a minor unit.  Those three ISO 4217 values are separated by commas.
  63  * The lines which start with '#'s are considered comment lines.  For example,


 170     private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
 171     // shift count for simple case country entry default currency digits
 172     private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
 173     // mask for special case country entries
 174     private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
 175     // mask for special case country index
 176     private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
 177     // delta from entry index component in main table to index into special case tables
 178     private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
 179     // mask for distinguishing simple and special case countries
 180     private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
 181     // mask for the numeric code of the currency
 182     private static final int NUMERIC_CODE_MASK = 0x0003FF00;
 183     // shift count for the numeric code of the currency
 184     private static final int NUMERIC_CODE_SHIFT = 8;
 185 
 186     // Currency data format version
 187     private static final int VALID_FORMAT_VERSION = 1;
 188 
 189     static {
 190         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 191             @Override
 192             public Void run() {
 193                 String homeDir = System.getProperty("java.home");
 194                 try {
 195                     String dataFile = homeDir + File.separator +
 196                             "lib" + File.separator + "currency.data";
 197                     try (DataInputStream dis = new DataInputStream(
 198                              new BufferedInputStream(
 199                              new FileInputStream(dataFile)))) {
 200                         if (dis.readInt() != MAGIC_NUMBER) {
 201                             throw new InternalError("Currency data is possibly corrupted");
 202                         }
 203                         formatVersion = dis.readInt();
 204                         if (formatVersion != VALID_FORMAT_VERSION) {
 205                             throw new InternalError("Currency data format is incorrect");
 206                         }
 207                         dataVersion = dis.readInt();
 208                         mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
 209                         int scCount = dis.readInt();
 210                         scCutOverTimes = readLongArray(dis, scCount);
 211                         scOldCurrencies = readStringArray(dis, scCount);
 212                         scNewCurrencies = readStringArray(dis, scCount);
 213                         scOldCurrenciesDFD = readIntArray(dis, scCount);
 214                         scNewCurrenciesDFD = readIntArray(dis, scCount);
 215                         scOldCurrenciesNumericCode = readIntArray(dis, scCount);
 216                         scNewCurrenciesNumericCode = readIntArray(dis, scCount);
 217                         int ocCount = dis.readInt();
 218                         otherCurrencies = dis.readUTF();
 219                         otherCurrenciesDFD = readIntArray(dis, ocCount);
 220                         otherCurrenciesNumericCode = readIntArray(dis, ocCount);
 221                     }
 222                 } catch (IOException e) {
 223                     throw new InternalError(e);
 224                 }
 225 
 226                 // look for the properties file for overrides
 227                 try {
 228                     File propFile = new File(homeDir + File.separator +
 229                                              "lib" + File.separator +
 230                                              "currency.properties");
 231                     if (propFile.exists()) {
 232                         Properties props = new Properties();
 233                         try (FileReader fr = new FileReader(propFile)) {
 234                             props.load(fr);
 235                         }
 236                         Set<String> keys = props.stringPropertyNames();
 237                         Pattern propertiesPattern =
 238                             Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
 239                         for (String key : keys) {
 240                            replaceCurrencyData(propertiesPattern,
 241                                key.toUpperCase(Locale.ROOT),


 324             new Currency(currencyCode, defaultFractionDigits, numericCode);
 325         instance = instances.putIfAbsent(currencyCode, currencyVal);
 326         return (instance != null ? instance : currencyVal);
 327     }
 328 
 329     /**
 330      * Returns the <code>Currency</code> instance for the country of the
 331      * given locale. The language and variant components of the locale
 332      * are ignored. The result may vary over time, as countries change their
 333      * currencies. For example, for the original member countries of the
 334      * European Monetary Union, the method returns the old national currencies
 335      * until December 31, 2001, and the Euro from January 1, 2002, local time
 336      * of the respective countries.
 337      * <p>
 338      * The method returns <code>null</code> for territories that don't
 339      * have a currency, such as Antarctica.
 340      *
 341      * @param locale the locale for whose country a <code>Currency</code>
 342      * instance is needed
 343      * @return the <code>Currency</code> instance for the country of the given
 344      * locale, or {@code null}
 345      * @exception NullPointerException if <code>locale</code> or its country
 346      * code is {@code null}
 347      * @exception IllegalArgumentException if the country of the given {@code locale}
 348      * is not a supported ISO 3166 country code.
 349      */
 350     public static Currency getInstance(Locale locale) {
 351         String country = locale.getCountry();
 352         if (country == null) {
 353             throw new NullPointerException();
 354         }
 355 
 356         if (country.length() != 2) {
 357             throw new IllegalArgumentException();
 358         }
 359 
 360         char char1 = country.charAt(0);
 361         char char2 = country.charAt(1);
 362         int tableEntry = getMainTableEntry(char1, char2);
 363         if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
 364                     && tableEntry != INVALID_COUNTRY_ENTRY) {
 365             char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
 366             int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
 367             int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
 368             StringBuilder sb = new StringBuilder(country);
 369             sb.append(finalChar);
 370             return getInstance(sb.toString(), defaultFractionDigits, numericCode);
 371         } else {
 372             // special cases
 373             if (tableEntry == INVALID_COUNTRY_ENTRY) {
 374                 throw new IllegalArgumentException();
 375             }
 376             if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
 377                 return null;
 378             } else {
 379                 int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
 380                 if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
 381                     return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
 382                         scOldCurrenciesNumericCode[index]);
 383                 } else {
 384                     return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
 385                         scNewCurrenciesNumericCode[index]);
 386                 }
 387             }
 388         }


 450      * symbol can be determined, the ISO 4217 currency code is returned.
 451      *
 452      * @return the symbol of this currency for the default locale
 453      */
 454     public String getSymbol() {
 455         return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
 456     }
 457 
 458     /**
 459      * Gets the symbol of this currency for the specified locale.
 460      * For example, for the US Dollar, the symbol is "$" if the specified
 461      * locale is the US, while for other locales it may be "US$". If no
 462      * symbol can be determined, the ISO 4217 currency code is returned.
 463      *
 464      * @param locale the locale for which a display name for this currency is
 465      * needed
 466      * @return the symbol of this currency for the specified locale
 467      * @exception NullPointerException if <code>locale</code> is null
 468      */
 469     public String getSymbol(Locale locale) {



 470         LocaleServiceProviderPool pool =
 471             LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);







 472         String symbol = pool.getLocalizedObject(
 473                                 CurrencyNameGetter.INSTANCE,
 474                                 locale, currencyCode, SYMBOL);

 475         if (symbol != null) {
 476             return symbol;
 477         }

 478 



 479         // use currency code as symbol of last resort
 480         return currencyCode;
 481     }

 482 
 483     /**
 484      * Gets the default number of fraction digits used with this currency.
 485      * For example, the default number of fraction digits for the Euro is 2,
 486      * while for the Japanese Yen it's 0.
 487      * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
 488      * -1 is returned.
 489      *
 490      * @return the default number of fraction digits used with this currency
 491      */
 492     public int getDefaultFractionDigits() {
 493         return defaultFractionDigits;
 494     }
 495 
 496     /**
 497      * Returns the ISO 4217 numeric code of this currency.
 498      *
 499      * @return the ISO 4217 numeric code of this currency
 500      * @since 1.7
 501      */


 510      *
 511      * @return the display name of this currency for the default locale
 512      * @since 1.7
 513      */
 514     public String getDisplayName() {
 515         return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
 516     }
 517 
 518     /**
 519      * Gets the name that is suitable for displaying this currency for
 520      * the specified locale.  If there is no suitable display name found
 521      * for the specified locale, the ISO 4217 currency code is returned.
 522      *
 523      * @param locale the locale for which a display name for this currency is
 524      * needed
 525      * @return the display name of this currency for the specified locale
 526      * @exception NullPointerException if <code>locale</code> is null
 527      * @since 1.7
 528      */
 529     public String getDisplayName(Locale locale) {







 530         LocaleServiceProviderPool pool =
 531             LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
 532         String result = pool.getLocalizedObject(

 533                                 CurrencyNameGetter.INSTANCE,
 534                                 locale, currencyCode, DISPLAYNAME);






 535         if (result != null) {
 536             return result;
 537         }



 538 
 539         // use currency code as symbol of last resort
 540         return currencyCode;
 541     }
 542 
 543     /**
 544      * Returns the ISO 4217 currency code of this currency.
 545      *
 546      * @return the ISO 4217 currency code of this currency
 547      */
 548     @Override
 549     public String toString() {
 550         return currencyCode;
 551     }
 552 
 553     /**
 554      * Resolves instances being deserialized to a single instance per currency.
 555      */
 556     private Object readResolve() {
 557         return getInstance(currencyCode);
 558     }
 559 
 560     /**
 561      * Gets the main table entry for the country whose country code consists
 562      * of char1 and char2.
 563      */
 564     private static int getMainTableEntry(char char1, char char2) {
 565         if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
 566             throw new IllegalArgumentException();
 567         }
 568         return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];


 571     /**
 572      * Sets the main table entry for the country whose country code consists
 573      * of char1 and char2.
 574      */
 575     private static void setMainTableEntry(char char1, char char2, int entry) {
 576         if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
 577             throw new IllegalArgumentException();
 578         }
 579         mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
 580     }
 581 
 582     /**
 583      * Obtains a localized currency names from a CurrencyNameProvider
 584      * implementation.
 585      */
 586     private static class CurrencyNameGetter
 587         implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
 588                                                                    String> {
 589         private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
 590 
 591         @Override
 592         public String getObject(CurrencyNameProvider currencyNameProvider,
 593                                 Locale locale,
 594                                 String key,
 595                                 Object... params) {
 596             assert params.length == 1;
 597             int type = (Integer)params[0];
 598 
 599             switch(type) {
 600             case SYMBOL:
 601                 return currencyNameProvider.getSymbol(key, locale);
 602             case DISPLAYNAME:
 603                 return currencyNameProvider.getDisplayName(key, locale);
 604             default:
 605                 assert false; // shouldn't happen
 606             }
 607 
 608             return null;
 609         }
 610     }
 611