0N/A/*
3909N/A * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.util;
0N/A
0N/Aimport java.io.BufferedInputStream;
0N/Aimport java.io.DataInputStream;
0N/Aimport java.io.File;
0N/Aimport java.io.FileInputStream;
0N/Aimport java.io.FileReader;
0N/Aimport java.io.IOException;
0N/Aimport java.io.Serializable;
0N/Aimport java.security.AccessController;
0N/Aimport java.security.PrivilegedAction;
0N/Aimport java.util.logging.Level;
0N/Aimport java.util.regex.Pattern;
0N/Aimport java.util.regex.Matcher;
0N/Aimport java.util.spi.CurrencyNameProvider;
0N/Aimport java.util.spi.LocaleServiceProvider;
0N/Aimport sun.util.LocaleServiceProviderPool;
1672N/Aimport sun.util.logging.PlatformLogger;
0N/Aimport sun.util.resources.LocaleData;
0N/Aimport sun.util.resources.OpenListResourceBundle;
0N/A
0N/A
0N/A/**
0N/A * Represents a currency. Currencies are identified by their ISO 4217 currency
0N/A * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
0N/A * ISO web site</a> for more information, including a table of
0N/A * currency codes.
0N/A * <p>
0N/A * The class is designed so that there's never more than one
0N/A * <code>Currency</code> instance for any given currency. Therefore, there's
0N/A * no public constructor. You obtain a <code>Currency</code> instance using
0N/A * the <code>getInstance</code> methods.
0N/A * <p>
0N/A * Users can supersede the Java runtime currency data by creating a properties
0N/A * file named <code>&lt;JAVA_HOME&gt;/lib/currency.properties</code>. The contents
0N/A * of the properties file are key/value pairs of the ISO 3166 country codes
0N/A * and the ISO 4217 currency data respectively. The value part consists of
0N/A * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
0N/A * code, and a minor unit. Those three ISO 4217 values are separated by commas.
0N/A * The lines which start with '#'s are considered comment lines. For example,
0N/A * <p>
0N/A * <code>
0N/A * #Sample currency properties<br>
0N/A * JP=JPZ,999,0
0N/A * </code>
0N/A * <p>
0N/A * will supersede the currency data for Japan.
0N/A *
0N/A * @since 1.4
0N/A */
0N/Apublic final class Currency implements Serializable {
0N/A
0N/A private static final long serialVersionUID = -158308464356906721L;
0N/A
0N/A /**
0N/A * ISO 4217 currency code for this currency.
0N/A *
0N/A * @serial
0N/A */
0N/A private final String currencyCode;
0N/A
0N/A /**
0N/A * Default fraction digits for this currency.
0N/A * Set from currency data tables.
0N/A */
0N/A transient private final int defaultFractionDigits;
0N/A
0N/A /**
0N/A * ISO 4217 numeric code for this currency.
0N/A * Set from currency data tables.
0N/A */
0N/A transient private final int numericCode;
0N/A
0N/A
0N/A // class data: instance map
0N/A
0N/A private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
0N/A private static HashSet<Currency> available;
0N/A
0N/A
0N/A // Class data: currency data obtained from currency.data file.
0N/A // Purpose:
0N/A // - determine valid country codes
0N/A // - determine valid currency codes
0N/A // - map country codes to currency codes
0N/A // - obtain default fraction digits for currency codes
0N/A //
0N/A // sc = special case; dfd = default fraction digits
0N/A // Simple countries are those where the country code is a prefix of the
0N/A // currency code, and there are no known plans to change the currency.
0N/A //
0N/A // table formats:
0N/A // - mainTable:
0N/A // - maps country code to 32-bit int
0N/A // - 26*26 entries, corresponding to [A-Z]*[A-Z]
0N/A // - \u007F -> not valid country
0N/A // - bits 18-31: unused
0N/A // - bits 8-17: numeric code (0 to 1023)
0N/A // - bit 7: 1 - special case, bits 0-4 indicate which one
0N/A // 0 - simple country, bits 0-4 indicate final char of currency code
0N/A // - bits 5-6: fraction digits for simple countries, 0 for special cases
0N/A // - bits 0-4: final char for currency code for simple country, or ID of special case
0N/A // - special case IDs:
0N/A // - 0: country has no currency
0N/A // - other: index into sc* arrays + 1
0N/A // - scCutOverTimes: cut-over time in millis as returned by
0N/A // System.currentTimeMillis for special case countries that are changing
0N/A // currencies; Long.MAX_VALUE for countries that are not changing currencies
0N/A // - scOldCurrencies: old currencies for special case countries
0N/A // - scNewCurrencies: new currencies for special case countries that are
0N/A // changing currencies; null for others
0N/A // - scOldCurrenciesDFD: default fraction digits for old currencies
0N/A // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
0N/A // countries that are not changing currencies
0N/A // - otherCurrencies: concatenation of all currency codes that are not the
0N/A // main currency of a simple country, separated by "-"
0N/A // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
0N/A
0N/A static int formatVersion;
0N/A static int dataVersion;
0N/A static int[] mainTable;
0N/A static long[] scCutOverTimes;
0N/A static String[] scOldCurrencies;
0N/A static String[] scNewCurrencies;
0N/A static int[] scOldCurrenciesDFD;
0N/A static int[] scNewCurrenciesDFD;
0N/A static int[] scOldCurrenciesNumericCode;
0N/A static int[] scNewCurrenciesNumericCode;
0N/A static String otherCurrencies;
0N/A static int[] otherCurrenciesDFD;
0N/A static int[] otherCurrenciesNumericCode;
0N/A
0N/A // handy constants - must match definitions in GenerateCurrencyData
0N/A // magic number
0N/A private static final int MAGIC_NUMBER = 0x43757244;
0N/A // number of characters from A to Z
0N/A private static final int A_TO_Z = ('Z' - 'A') + 1;
0N/A // entry for invalid country codes
0N/A private static final int INVALID_COUNTRY_ENTRY = 0x007F;
0N/A // entry for countries without currency
0N/A private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x0080;
0N/A // mask for simple case country entries
0N/A private static final int SIMPLE_CASE_COUNTRY_MASK = 0x0000;
0N/A // mask for simple case country entry final character
0N/A private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x001F;
0N/A // mask for simple case country entry default currency digits
0N/A private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
0N/A // shift count for simple case country entry default currency digits
0N/A private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
0N/A // mask for special case country entries
0N/A private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
0N/A // mask for special case country index
0N/A private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
0N/A // delta from entry index component in main table to index into special case tables
0N/A private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
0N/A // mask for distinguishing simple and special case countries
0N/A private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
0N/A // mask for the numeric code of the currency
0N/A private static final int NUMERIC_CODE_MASK = 0x0003FF00;
0N/A // shift count for the numeric code of the currency
0N/A private static final int NUMERIC_CODE_SHIFT = 8;
0N/A
0N/A // Currency data format version
0N/A private static final int VALID_FORMAT_VERSION = 1;
0N/A
0N/A static {
4565N/A AccessController.doPrivileged(new PrivilegedAction<Object>() {
0N/A public Object run() {
0N/A String homeDir = System.getProperty("java.home");
0N/A try {
0N/A String dataFile = homeDir + File.separator +
0N/A "lib" + File.separator + "currency.data";
0N/A DataInputStream dis = new DataInputStream(
0N/A new BufferedInputStream(
0N/A new FileInputStream(dataFile)));
0N/A if (dis.readInt() != MAGIC_NUMBER) {
0N/A throw new InternalError("Currency data is possibly corrupted");
0N/A }
0N/A formatVersion = dis.readInt();
0N/A if (formatVersion != VALID_FORMAT_VERSION) {
0N/A throw new InternalError("Currency data format is incorrect");
0N/A }
0N/A dataVersion = dis.readInt();
0N/A mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
0N/A int scCount = dis.readInt();
0N/A scCutOverTimes = readLongArray(dis, scCount);
0N/A scOldCurrencies = readStringArray(dis, scCount);
0N/A scNewCurrencies = readStringArray(dis, scCount);
0N/A scOldCurrenciesDFD = readIntArray(dis, scCount);
0N/A scNewCurrenciesDFD = readIntArray(dis, scCount);
0N/A scOldCurrenciesNumericCode = readIntArray(dis, scCount);
0N/A scNewCurrenciesNumericCode = readIntArray(dis, scCount);
0N/A int ocCount = dis.readInt();
0N/A otherCurrencies = dis.readUTF();
0N/A otherCurrenciesDFD = readIntArray(dis, ocCount);
0N/A otherCurrenciesNumericCode = readIntArray(dis, ocCount);
0N/A dis.close();
0N/A } catch (IOException e) {
0N/A InternalError ie = new InternalError();
0N/A ie.initCause(e);
0N/A throw ie;
0N/A }
0N/A
0N/A // look for the properties file for overrides
0N/A try {
0N/A File propFile = new File(homeDir + File.separator +
0N/A "lib" + File.separator +
0N/A "currency.properties");
0N/A if (propFile.exists()) {
0N/A Properties props = new Properties();
3646N/A try (FileReader fr = new FileReader(propFile)) {
3646N/A props.load(fr);
3646N/A }
0N/A Set<String> keys = props.stringPropertyNames();
0N/A Pattern propertiesPattern =
0N/A Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
0N/A for (String key : keys) {
0N/A replaceCurrencyData(propertiesPattern,
0N/A key.toUpperCase(Locale.ROOT),
0N/A props.getProperty(key).toUpperCase(Locale.ROOT));
0N/A }
0N/A }
0N/A } catch (IOException e) {
1672N/A info("currency.properties is ignored because of an IOException", e);
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A }
0N/A
0N/A /**
0N/A * Constants for retrieving localized names from the name providers.
0N/A */
0N/A private static final int SYMBOL = 0;
0N/A private static final int DISPLAYNAME = 1;
0N/A
0N/A
0N/A /**
0N/A * Constructs a <code>Currency</code> instance. The constructor is private
0N/A * so that we can insure that there's never more than one instance for a
0N/A * given currency.
0N/A */
0N/A private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
0N/A this.currencyCode = currencyCode;
0N/A this.defaultFractionDigits = defaultFractionDigits;
0N/A this.numericCode = numericCode;
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>Currency</code> instance for the given currency code.
0N/A *
0N/A * @param currencyCode the ISO 4217 code of the currency
0N/A * @return the <code>Currency</code> instance for the given currency code
0N/A * @exception NullPointerException if <code>currencyCode</code> is null
0N/A * @exception IllegalArgumentException if <code>currencyCode</code> is not
0N/A * a supported ISO 4217 code.
0N/A */
0N/A public static Currency getInstance(String currencyCode) {
0N/A return getInstance(currencyCode, Integer.MIN_VALUE, 0);
0N/A }
0N/A
0N/A private static Currency getInstance(String currencyCode, int defaultFractionDigits,
0N/A int numericCode) {
0N/A synchronized (instances) {
0N/A // Try to look up the currency code in the instances table.
0N/A // This does the null pointer check as a side effect.
0N/A // Also, if there already is an entry, the currencyCode must be valid.
0N/A Currency instance = instances.get(currencyCode);
0N/A if (instance != null) {
0N/A return instance;
0N/A }
0N/A
0N/A if (defaultFractionDigits == Integer.MIN_VALUE) {
0N/A // Currency code not internally generated, need to verify first
0N/A // A currency code must have 3 characters and exist in the main table
0N/A // or in the list of other currencies.
0N/A if (currencyCode.length() != 3) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A char char1 = currencyCode.charAt(0);
0N/A char char2 = currencyCode.charAt(1);
0N/A int tableEntry = getMainTableEntry(char1, char2);
0N/A if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
0N/A && tableEntry != INVALID_COUNTRY_ENTRY
0N/A && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
0N/A defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
0N/A numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
0N/A } else {
0N/A // Check for '-' separately so we don't get false hits in the table.
0N/A if (currencyCode.charAt(2) == '-') {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A int index = otherCurrencies.indexOf(currencyCode);
0N/A if (index == -1) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A defaultFractionDigits = otherCurrenciesDFD[index / 4];
0N/A numericCode = otherCurrenciesNumericCode[index / 4];
0N/A }
0N/A }
0N/A
0N/A instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
0N/A instances.put(currencyCode, instance);
0N/A return instance;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>Currency</code> instance for the country of the
0N/A * given locale. The language and variant components of the locale
0N/A * are ignored. The result may vary over time, as countries change their
0N/A * currencies. For example, for the original member countries of the
0N/A * European Monetary Union, the method returns the old national currencies
0N/A * until December 31, 2001, and the Euro from January 1, 2002, local time
0N/A * of the respective countries.
0N/A * <p>
0N/A * The method returns <code>null</code> for territories that don't
0N/A * have a currency, such as Antarctica.
0N/A *
0N/A * @param locale the locale for whose country a <code>Currency</code>
0N/A * instance is needed
0N/A * @return the <code>Currency</code> instance for the country of the given
0N/A * locale, or null
0N/A * @exception NullPointerException if <code>locale</code> or its country
0N/A * code is null
0N/A * @exception IllegalArgumentException if the country of the given locale
0N/A * is not a supported ISO 3166 country code.
0N/A */
0N/A public static Currency getInstance(Locale locale) {
0N/A String country = locale.getCountry();
0N/A if (country == null) {
0N/A throw new NullPointerException();
0N/A }
0N/A
0N/A if (country.length() != 2) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A char char1 = country.charAt(0);
0N/A char char2 = country.charAt(1);
0N/A int tableEntry = getMainTableEntry(char1, char2);
0N/A if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
0N/A && tableEntry != INVALID_COUNTRY_ENTRY) {
0N/A char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
0N/A int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
0N/A int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
0N/A StringBuffer sb = new StringBuffer(country);
0N/A sb.append(finalChar);
0N/A return getInstance(sb.toString(), defaultFractionDigits, numericCode);
0N/A } else {
0N/A // special cases
0N/A if (tableEntry == INVALID_COUNTRY_ENTRY) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
0N/A return null;
0N/A } else {
0N/A int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
0N/A if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
0N/A return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
0N/A scOldCurrenciesNumericCode[index]);
0N/A } else {
0N/A return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
0N/A scNewCurrenciesNumericCode[index]);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gets the set of available currencies. The returned set of currencies
0N/A * contains all of the available currencies, which may include currencies
0N/A * that represent obsolete ISO 4217 codes. The set can be modified
0N/A * without affecting the available currencies in the runtime.
0N/A *
0N/A * @return the set of available currencies. If there is no currency
0N/A * available in the runtime, the returned set is empty.
0N/A * @since 1.7
0N/A */
0N/A public static Set<Currency> getAvailableCurrencies() {
0N/A synchronized(Currency.class) {
0N/A if (available == null) {
0N/A available = new HashSet<Currency>(256);
0N/A
0N/A // Add simple currencies first
0N/A for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
0N/A for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
0N/A int tableEntry = getMainTableEntry(c1, c2);
0N/A if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
0N/A && tableEntry != INVALID_COUNTRY_ENTRY) {
0N/A char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
0N/A int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
0N/A int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
0N/A StringBuilder sb = new StringBuilder();
0N/A sb.append(c1);
0N/A sb.append(c2);
0N/A sb.append(finalChar);
0N/A available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Now add other currencies
0N/A StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
0N/A while (st.hasMoreElements()) {
0N/A available.add(getInstance((String)st.nextElement()));
0N/A }
0N/A }
0N/A }
0N/A
4565N/A @SuppressWarnings("unchecked")
4565N/A Set<Currency> result = (Set<Currency>) available.clone();
4565N/A return result;
0N/A }
0N/A
0N/A /**
0N/A * Gets the ISO 4217 currency code of this currency.
0N/A *
0N/A * @return the ISO 4217 currency code of this currency.
0N/A */
0N/A public String getCurrencyCode() {
0N/A return currencyCode;
0N/A }
0N/A
0N/A /**
0N/A * Gets the symbol of this currency for the default locale.
0N/A * For example, for the US Dollar, the symbol is "$" if the default
0N/A * locale is the US, while for other locales it may be "US$". If no
0N/A * symbol can be determined, the ISO 4217 currency code is returned.
0N/A *
0N/A * @return the symbol of this currency for the default locale
0N/A */
0N/A public String getSymbol() {
2700N/A return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
0N/A }
0N/A
0N/A /**
0N/A * Gets the symbol of this currency for the specified locale.
0N/A * For example, for the US Dollar, the symbol is "$" if the specified
0N/A * locale is the US, while for other locales it may be "US$". If no
0N/A * symbol can be determined, the ISO 4217 currency code is returned.
0N/A *
0N/A * @param locale the locale for which a display name for this currency is
0N/A * needed
0N/A * @return the symbol of this currency for the specified locale
0N/A * @exception NullPointerException if <code>locale</code> is null
0N/A */
0N/A public String getSymbol(Locale locale) {
0N/A try {
0N/A // Check whether a provider can provide an implementation that's closer
0N/A // to the requested locale than what the Java runtime itself can provide.
0N/A LocaleServiceProviderPool pool =
0N/A LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
0N/A
0N/A if (pool.hasProviders()) {
0N/A // Assuming that all the country locales include necessary currency
0N/A // symbols in the Java runtime's resources, so there is no need to
0N/A // examine whether Java runtime's currency resource bundle is missing
0N/A // names. Therefore, no resource bundle is provided for calling this
0N/A // method.
0N/A String symbol = pool.getLocalizedObject(
0N/A CurrencyNameGetter.INSTANCE,
0N/A locale, (OpenListResourceBundle)null,
0N/A currencyCode, SYMBOL);
0N/A if (symbol != null) {
0N/A return symbol;
0N/A }
0N/A }
0N/A
0N/A ResourceBundle bundle = LocaleData.getCurrencyNames(locale);
0N/A return bundle.getString(currencyCode);
0N/A } catch (MissingResourceException e) {
0N/A // use currency code as symbol of last resort
0N/A return currencyCode;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gets the default number of fraction digits used with this currency.
0N/A * For example, the default number of fraction digits for the Euro is 2,
0N/A * while for the Japanese Yen it's 0.
0N/A * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
0N/A * -1 is returned.
0N/A *
0N/A * @return the default number of fraction digits used with this currency
0N/A */
0N/A public int getDefaultFractionDigits() {
0N/A return defaultFractionDigits;
0N/A }
0N/A
0N/A /**
0N/A * Returns the ISO 4217 numeric code of this currency.
0N/A *
0N/A * @return the ISO 4217 numeric code of this currency
0N/A * @since 1.7
0N/A */
0N/A public int getNumericCode() {
0N/A return numericCode;
0N/A }
0N/A
0N/A /**
0N/A * Gets the name that is suitable for displaying this currency for
0N/A * the default locale. If there is no suitable display name found
0N/A * for the default locale, the ISO 4217 currency code is returned.
0N/A *
0N/A * @return the display name of this currency for the default locale
0N/A * @since 1.7
0N/A */
0N/A public String getDisplayName() {
2700N/A return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
0N/A }
0N/A
0N/A /**
0N/A * Gets the name that is suitable for displaying this currency for
0N/A * the specified locale. If there is no suitable display name found
0N/A * for the specified locale, the ISO 4217 currency code is returned.
0N/A *
0N/A * @param locale the locale for which a display name for this currency is
0N/A * needed
0N/A * @return the display name of this currency for the specified locale
0N/A * @exception NullPointerException if <code>locale</code> is null
0N/A * @since 1.7
0N/A */
0N/A public String getDisplayName(Locale locale) {
0N/A try {
0N/A OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
0N/A String result = null;
0N/A String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
0N/A
0N/A // Check whether a provider can provide an implementation that's closer
0N/A // to the requested locale than what the Java runtime itself can provide.
0N/A LocaleServiceProviderPool pool =
0N/A LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
0N/A if (pool.hasProviders()) {
0N/A result = pool.getLocalizedObject(
0N/A CurrencyNameGetter.INSTANCE,
0N/A locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
0N/A }
0N/A
0N/A if (result == null) {
0N/A result = bundle.getString(bundleKey);
0N/A }
0N/A
0N/A if (result != null) {
0N/A return result;
0N/A }
0N/A } catch (MissingResourceException e) {
0N/A // fall through
0N/A }
0N/A
0N/A // use currency code as symbol of last resort
0N/A return currencyCode;
0N/A }
0N/A
0N/A /**
0N/A * Returns the ISO 4217 currency code of this currency.
0N/A *
0N/A * @return the ISO 4217 currency code of this currency
0N/A */
0N/A public String toString() {
0N/A return currencyCode;
0N/A }
0N/A
0N/A /**
0N/A * Resolves instances being deserialized to a single instance per currency.
0N/A */
0N/A private Object readResolve() {
0N/A return getInstance(currencyCode);
0N/A }
0N/A
0N/A /**
0N/A * Gets the main table entry for the country whose country code consists
0N/A * of char1 and char2.
0N/A */
0N/A private static int getMainTableEntry(char char1, char char2) {
0N/A if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
0N/A }
0N/A
0N/A /**
0N/A * Sets the main table entry for the country whose country code consists
0N/A * of char1 and char2.
0N/A */
0N/A private static void setMainTableEntry(char char1, char char2, int entry) {
0N/A if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
0N/A }
0N/A
0N/A /**
0N/A * Obtains a localized currency names from a CurrencyNameProvider
0N/A * implementation.
0N/A */
0N/A private static class CurrencyNameGetter
0N/A implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
0N/A String> {
0N/A private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
0N/A
0N/A public String getObject(CurrencyNameProvider currencyNameProvider,
0N/A Locale locale,
0N/A String key,
0N/A Object... params) {
0N/A assert params.length == 1;
0N/A int type = (Integer)params[0];
0N/A
0N/A switch(type) {
0N/A case SYMBOL:
0N/A return currencyNameProvider.getSymbol(key, locale);
0N/A case DISPLAYNAME:
0N/A return currencyNameProvider.getDisplayName(key, locale);
0N/A default:
0N/A assert false; // shouldn't happen
0N/A }
0N/A
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
0N/A int[] ret = new int[count];
0N/A for (int i = 0; i < count; i++) {
0N/A ret[i] = dis.readInt();
0N/A }
0N/A
0N/A return ret;
0N/A }
0N/A
0N/A private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
0N/A long[] ret = new long[count];
0N/A for (int i = 0; i < count; i++) {
0N/A ret[i] = dis.readLong();
0N/A }
0N/A
0N/A return ret;
0N/A }
0N/A
0N/A private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
0N/A String[] ret = new String[count];
0N/A for (int i = 0; i < count; i++) {
0N/A ret[i] = dis.readUTF();
0N/A }
0N/A
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Replaces currency data found in the currencydata.properties file
0N/A *
0N/A * @param pattern regex pattern for the properties
0N/A * @param ctry country code
0N/A * @param data currency data. This is a comma separated string that
0N/A * consists of "three-letter alphabet code", "three-digit numeric code",
0N/A * and "one-digit (0,1,2, or 3) default fraction digit".
0N/A * For example, "JPZ,392,0".
0N/A * @throws
0N/A */
0N/A private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
0N/A
0N/A if (ctry.length() != 2) {
0N/A // ignore invalid country code
0N/A String message = new StringBuilder()
0N/A .append("The entry in currency.properties for ")
0N/A .append(ctry).append(" is ignored because of the invalid country code.")
0N/A .toString();
1672N/A info(message, null);
0N/A return;
0N/A }
0N/A
0N/A Matcher m = pattern.matcher(curdata);
0N/A if (!m.find()) {
0N/A // format is not recognized. ignore the data
0N/A String message = new StringBuilder()
0N/A .append("The entry in currency.properties for ")
0N/A .append(ctry)
0N/A .append(" is ignored because the value format is not recognized.")
0N/A .toString();
1672N/A info(message, null);
0N/A return;
0N/A }
0N/A
0N/A String code = m.group(1);
0N/A int numeric = Integer.parseInt(m.group(2));
0N/A int fraction = Integer.parseInt(m.group(3));
0N/A int entry = numeric << NUMERIC_CODE_SHIFT;
0N/A
0N/A int index;
0N/A for (index = 0; index < scOldCurrencies.length; index++) {
0N/A if (scOldCurrencies[index].equals(code)) {
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (index == scOldCurrencies.length) {
0N/A // simple case
0N/A entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
0N/A (code.charAt(2) - 'A');
0N/A } else {
0N/A // special case
0N/A entry |= SPECIAL_CASE_COUNTRY_MASK |
0N/A (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
0N/A }
0N/A setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
0N/A }
0N/A
1672N/A private static void info(String message, Throwable t) {
1672N/A PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
1672N/A if (logger.isLoggable(PlatformLogger.INFO)) {
0N/A if (t != null) {
1672N/A logger.info(message, t);
0N/A } else {
1672N/A logger.info(message);
0N/A }
0N/A }
0N/A }
0N/A}