rt/emul/compact/src/main/java/java/util/Currency.java
branchjdk7-b147
changeset 1334 588d5bf7a560
child 1340 41046f76a76a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/rt/emul/compact/src/main/java/java/util/Currency.java	Thu Oct 03 15:40:35 2013 +0200
     1.3 @@ -0,0 +1,741 @@
     1.4 +/*
     1.5 + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Oracle designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Oracle in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.25 + * or visit www.oracle.com if you need additional information or have any
    1.26 + * questions.
    1.27 + */
    1.28 +
    1.29 +package java.util;
    1.30 +
    1.31 +import java.io.BufferedInputStream;
    1.32 +import java.io.DataInputStream;
    1.33 +import java.io.File;
    1.34 +import java.io.FileInputStream;
    1.35 +import java.io.FileReader;
    1.36 +import java.io.IOException;
    1.37 +import java.io.Serializable;
    1.38 +import java.security.AccessController;
    1.39 +import java.security.PrivilegedAction;
    1.40 +import java.util.logging.Level;
    1.41 +import java.util.regex.Pattern;
    1.42 +import java.util.regex.Matcher;
    1.43 +import java.util.spi.CurrencyNameProvider;
    1.44 +import java.util.spi.LocaleServiceProvider;
    1.45 +import sun.util.LocaleServiceProviderPool;
    1.46 +import sun.util.logging.PlatformLogger;
    1.47 +import sun.util.resources.LocaleData;
    1.48 +import sun.util.resources.OpenListResourceBundle;
    1.49 +
    1.50 +
    1.51 +/**
    1.52 + * Represents a currency. Currencies are identified by their ISO 4217 currency
    1.53 + * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
    1.54 + * ISO web site</a> for more information, including a table of
    1.55 + * currency codes.
    1.56 + * <p>
    1.57 + * The class is designed so that there's never more than one
    1.58 + * <code>Currency</code> instance for any given currency. Therefore, there's
    1.59 + * no public constructor. You obtain a <code>Currency</code> instance using
    1.60 + * the <code>getInstance</code> methods.
    1.61 + * <p>
    1.62 + * Users can supersede the Java runtime currency data by creating a properties
    1.63 + * file named <code>&lt;JAVA_HOME&gt;/lib/currency.properties</code>.  The contents
    1.64 + * of the properties file are key/value pairs of the ISO 3166 country codes
    1.65 + * and the ISO 4217 currency data respectively.  The value part consists of
    1.66 + * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
    1.67 + * code, and a minor unit.  Those three ISO 4217 values are separated by commas.
    1.68 + * The lines which start with '#'s are considered comment lines.  For example,
    1.69 + * <p>
    1.70 + * <code>
    1.71 + * #Sample currency properties<br>
    1.72 + * JP=JPZ,999,0
    1.73 + * </code>
    1.74 + * <p>
    1.75 + * will supersede the currency data for Japan.
    1.76 + *
    1.77 + * @since 1.4
    1.78 + */
    1.79 +public final class Currency implements Serializable {
    1.80 +
    1.81 +    private static final long serialVersionUID = -158308464356906721L;
    1.82 +
    1.83 +    /**
    1.84 +     * ISO 4217 currency code for this currency.
    1.85 +     *
    1.86 +     * @serial
    1.87 +     */
    1.88 +    private final String currencyCode;
    1.89 +
    1.90 +    /**
    1.91 +     * Default fraction digits for this currency.
    1.92 +     * Set from currency data tables.
    1.93 +     */
    1.94 +    transient private final int defaultFractionDigits;
    1.95 +
    1.96 +    /**
    1.97 +     * ISO 4217 numeric code for this currency.
    1.98 +     * Set from currency data tables.
    1.99 +     */
   1.100 +    transient private final int numericCode;
   1.101 +
   1.102 +
   1.103 +    // class data: instance map
   1.104 +
   1.105 +    private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
   1.106 +    private static HashSet<Currency> available;
   1.107 +
   1.108 +
   1.109 +    // Class data: currency data obtained from currency.data file.
   1.110 +    // Purpose:
   1.111 +    // - determine valid country codes
   1.112 +    // - determine valid currency codes
   1.113 +    // - map country codes to currency codes
   1.114 +    // - obtain default fraction digits for currency codes
   1.115 +    //
   1.116 +    // sc = special case; dfd = default fraction digits
   1.117 +    // Simple countries are those where the country code is a prefix of the
   1.118 +    // currency code, and there are no known plans to change the currency.
   1.119 +    //
   1.120 +    // table formats:
   1.121 +    // - mainTable:
   1.122 +    //   - maps country code to 32-bit int
   1.123 +    //   - 26*26 entries, corresponding to [A-Z]*[A-Z]
   1.124 +    //   - \u007F -> not valid country
   1.125 +    //   - bits 18-31: unused
   1.126 +    //   - bits 8-17: numeric code (0 to 1023)
   1.127 +    //   - bit 7: 1 - special case, bits 0-4 indicate which one
   1.128 +    //            0 - simple country, bits 0-4 indicate final char of currency code
   1.129 +    //   - bits 5-6: fraction digits for simple countries, 0 for special cases
   1.130 +    //   - bits 0-4: final char for currency code for simple country, or ID of special case
   1.131 +    // - special case IDs:
   1.132 +    //   - 0: country has no currency
   1.133 +    //   - other: index into sc* arrays + 1
   1.134 +    // - scCutOverTimes: cut-over time in millis as returned by
   1.135 +    //   System.currentTimeMillis for special case countries that are changing
   1.136 +    //   currencies; Long.MAX_VALUE for countries that are not changing currencies
   1.137 +    // - scOldCurrencies: old currencies for special case countries
   1.138 +    // - scNewCurrencies: new currencies for special case countries that are
   1.139 +    //   changing currencies; null for others
   1.140 +    // - scOldCurrenciesDFD: default fraction digits for old currencies
   1.141 +    // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
   1.142 +    //   countries that are not changing currencies
   1.143 +    // - otherCurrencies: concatenation of all currency codes that are not the
   1.144 +    //   main currency of a simple country, separated by "-"
   1.145 +    // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
   1.146 +
   1.147 +    static int formatVersion;
   1.148 +    static int dataVersion;
   1.149 +    static int[] mainTable;
   1.150 +    static long[] scCutOverTimes;
   1.151 +    static String[] scOldCurrencies;
   1.152 +    static String[] scNewCurrencies;
   1.153 +    static int[] scOldCurrenciesDFD;
   1.154 +    static int[] scNewCurrenciesDFD;
   1.155 +    static int[] scOldCurrenciesNumericCode;
   1.156 +    static int[] scNewCurrenciesNumericCode;
   1.157 +    static String otherCurrencies;
   1.158 +    static int[] otherCurrenciesDFD;
   1.159 +    static int[] otherCurrenciesNumericCode;
   1.160 +
   1.161 +    // handy constants - must match definitions in GenerateCurrencyData
   1.162 +    // magic number
   1.163 +    private static final int MAGIC_NUMBER = 0x43757244;
   1.164 +    // number of characters from A to Z
   1.165 +    private static final int A_TO_Z = ('Z' - 'A') + 1;
   1.166 +    // entry for invalid country codes
   1.167 +    private static final int INVALID_COUNTRY_ENTRY = 0x007F;
   1.168 +    // entry for countries without currency
   1.169 +    private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x0080;
   1.170 +    // mask for simple case country entries
   1.171 +    private static final int SIMPLE_CASE_COUNTRY_MASK = 0x0000;
   1.172 +    // mask for simple case country entry final character
   1.173 +    private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x001F;
   1.174 +    // mask for simple case country entry default currency digits
   1.175 +    private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
   1.176 +    // shift count for simple case country entry default currency digits
   1.177 +    private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
   1.178 +    // mask for special case country entries
   1.179 +    private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
   1.180 +    // mask for special case country index
   1.181 +    private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
   1.182 +    // delta from entry index component in main table to index into special case tables
   1.183 +    private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
   1.184 +    // mask for distinguishing simple and special case countries
   1.185 +    private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
   1.186 +    // mask for the numeric code of the currency
   1.187 +    private static final int NUMERIC_CODE_MASK = 0x0003FF00;
   1.188 +    // shift count for the numeric code of the currency
   1.189 +    private static final int NUMERIC_CODE_SHIFT = 8;
   1.190 +
   1.191 +    // Currency data format version
   1.192 +    private static final int VALID_FORMAT_VERSION = 1;
   1.193 +
   1.194 +    static {
   1.195 +        AccessController.doPrivileged(new PrivilegedAction() {
   1.196 +            public Object run() {
   1.197 +                String homeDir = System.getProperty("java.home");
   1.198 +                try {
   1.199 +                    String dataFile = homeDir + File.separator +
   1.200 +                            "lib" + File.separator + "currency.data";
   1.201 +                    DataInputStream dis = new DataInputStream(
   1.202 +                        new BufferedInputStream(
   1.203 +                        new FileInputStream(dataFile)));
   1.204 +                    if (dis.readInt() != MAGIC_NUMBER) {
   1.205 +                        throw new InternalError("Currency data is possibly corrupted");
   1.206 +                    }
   1.207 +                    formatVersion = dis.readInt();
   1.208 +                    if (formatVersion != VALID_FORMAT_VERSION) {
   1.209 +                        throw new InternalError("Currency data format is incorrect");
   1.210 +                    }
   1.211 +                    dataVersion = dis.readInt();
   1.212 +                    mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
   1.213 +                    int scCount = dis.readInt();
   1.214 +                    scCutOverTimes = readLongArray(dis, scCount);
   1.215 +                    scOldCurrencies = readStringArray(dis, scCount);
   1.216 +                    scNewCurrencies = readStringArray(dis, scCount);
   1.217 +                    scOldCurrenciesDFD = readIntArray(dis, scCount);
   1.218 +                    scNewCurrenciesDFD = readIntArray(dis, scCount);
   1.219 +                    scOldCurrenciesNumericCode = readIntArray(dis, scCount);
   1.220 +                    scNewCurrenciesNumericCode = readIntArray(dis, scCount);
   1.221 +                    int ocCount = dis.readInt();
   1.222 +                    otherCurrencies = dis.readUTF();
   1.223 +                    otherCurrenciesDFD = readIntArray(dis, ocCount);
   1.224 +                    otherCurrenciesNumericCode = readIntArray(dis, ocCount);
   1.225 +                    dis.close();
   1.226 +                } catch (IOException e) {
   1.227 +                    InternalError ie = new InternalError();
   1.228 +                    ie.initCause(e);
   1.229 +                    throw ie;
   1.230 +                }
   1.231 +
   1.232 +                // look for the properties file for overrides
   1.233 +                try {
   1.234 +                    File propFile = new File(homeDir + File.separator +
   1.235 +                                             "lib" + File.separator +
   1.236 +                                             "currency.properties");
   1.237 +                    if (propFile.exists()) {
   1.238 +                        Properties props = new Properties();
   1.239 +                        try (FileReader fr = new FileReader(propFile)) {
   1.240 +                            props.load(fr);
   1.241 +                        }
   1.242 +                        Set<String> keys = props.stringPropertyNames();
   1.243 +                        Pattern propertiesPattern =
   1.244 +                            Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
   1.245 +                        for (String key : keys) {
   1.246 +                           replaceCurrencyData(propertiesPattern,
   1.247 +                               key.toUpperCase(Locale.ROOT),
   1.248 +                               props.getProperty(key).toUpperCase(Locale.ROOT));
   1.249 +                        }
   1.250 +                    }
   1.251 +                } catch (IOException e) {
   1.252 +                    info("currency.properties is ignored because of an IOException", e);
   1.253 +                }
   1.254 +                return null;
   1.255 +            }
   1.256 +        });
   1.257 +    }
   1.258 +
   1.259 +    /**
   1.260 +     * Constants for retrieving localized names from the name providers.
   1.261 +     */
   1.262 +    private static final int SYMBOL = 0;
   1.263 +    private static final int DISPLAYNAME = 1;
   1.264 +
   1.265 +
   1.266 +    /**
   1.267 +     * Constructs a <code>Currency</code> instance. The constructor is private
   1.268 +     * so that we can insure that there's never more than one instance for a
   1.269 +     * given currency.
   1.270 +     */
   1.271 +    private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
   1.272 +        this.currencyCode = currencyCode;
   1.273 +        this.defaultFractionDigits = defaultFractionDigits;
   1.274 +        this.numericCode = numericCode;
   1.275 +    }
   1.276 +
   1.277 +    /**
   1.278 +     * Returns the <code>Currency</code> instance for the given currency code.
   1.279 +     *
   1.280 +     * @param currencyCode the ISO 4217 code of the currency
   1.281 +     * @return the <code>Currency</code> instance for the given currency code
   1.282 +     * @exception NullPointerException if <code>currencyCode</code> is null
   1.283 +     * @exception IllegalArgumentException if <code>currencyCode</code> is not
   1.284 +     * a supported ISO 4217 code.
   1.285 +     */
   1.286 +    public static Currency getInstance(String currencyCode) {
   1.287 +        return getInstance(currencyCode, Integer.MIN_VALUE, 0);
   1.288 +    }
   1.289 +
   1.290 +    private static Currency getInstance(String currencyCode, int defaultFractionDigits,
   1.291 +        int numericCode) {
   1.292 +        synchronized (instances) {
   1.293 +            // Try to look up the currency code in the instances table.
   1.294 +            // This does the null pointer check as a side effect.
   1.295 +            // Also, if there already is an entry, the currencyCode must be valid.
   1.296 +            Currency instance = instances.get(currencyCode);
   1.297 +            if (instance != null) {
   1.298 +                return instance;
   1.299 +            }
   1.300 +
   1.301 +            if (defaultFractionDigits == Integer.MIN_VALUE) {
   1.302 +                // Currency code not internally generated, need to verify first
   1.303 +                // A currency code must have 3 characters and exist in the main table
   1.304 +                // or in the list of other currencies.
   1.305 +                if (currencyCode.length() != 3) {
   1.306 +                    throw new IllegalArgumentException();
   1.307 +                }
   1.308 +                char char1 = currencyCode.charAt(0);
   1.309 +                char char2 = currencyCode.charAt(1);
   1.310 +                int tableEntry = getMainTableEntry(char1, char2);
   1.311 +                if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
   1.312 +                        && tableEntry != INVALID_COUNTRY_ENTRY
   1.313 +                        && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
   1.314 +                    defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
   1.315 +                    numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
   1.316 +                } else {
   1.317 +                    // Check for '-' separately so we don't get false hits in the table.
   1.318 +                    if (currencyCode.charAt(2) == '-') {
   1.319 +                        throw new IllegalArgumentException();
   1.320 +                    }
   1.321 +                    int index = otherCurrencies.indexOf(currencyCode);
   1.322 +                    if (index == -1) {
   1.323 +                        throw new IllegalArgumentException();
   1.324 +                    }
   1.325 +                    defaultFractionDigits = otherCurrenciesDFD[index / 4];
   1.326 +                    numericCode = otherCurrenciesNumericCode[index / 4];
   1.327 +                }
   1.328 +            }
   1.329 +
   1.330 +            instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
   1.331 +            instances.put(currencyCode, instance);
   1.332 +            return instance;
   1.333 +        }
   1.334 +    }
   1.335 +
   1.336 +    /**
   1.337 +     * Returns the <code>Currency</code> instance for the country of the
   1.338 +     * given locale. The language and variant components of the locale
   1.339 +     * are ignored. The result may vary over time, as countries change their
   1.340 +     * currencies. For example, for the original member countries of the
   1.341 +     * European Monetary Union, the method returns the old national currencies
   1.342 +     * until December 31, 2001, and the Euro from January 1, 2002, local time
   1.343 +     * of the respective countries.
   1.344 +     * <p>
   1.345 +     * The method returns <code>null</code> for territories that don't
   1.346 +     * have a currency, such as Antarctica.
   1.347 +     *
   1.348 +     * @param locale the locale for whose country a <code>Currency</code>
   1.349 +     * instance is needed
   1.350 +     * @return the <code>Currency</code> instance for the country of the given
   1.351 +     * locale, or null
   1.352 +     * @exception NullPointerException if <code>locale</code> or its country
   1.353 +     * code is null
   1.354 +     * @exception IllegalArgumentException if the country of the given locale
   1.355 +     * is not a supported ISO 3166 country code.
   1.356 +     */
   1.357 +    public static Currency getInstance(Locale locale) {
   1.358 +        String country = locale.getCountry();
   1.359 +        if (country == null) {
   1.360 +            throw new NullPointerException();
   1.361 +        }
   1.362 +
   1.363 +        if (country.length() != 2) {
   1.364 +            throw new IllegalArgumentException();
   1.365 +        }
   1.366 +
   1.367 +        char char1 = country.charAt(0);
   1.368 +        char char2 = country.charAt(1);
   1.369 +        int tableEntry = getMainTableEntry(char1, char2);
   1.370 +        if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
   1.371 +                    && tableEntry != INVALID_COUNTRY_ENTRY) {
   1.372 +            char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
   1.373 +            int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
   1.374 +            int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
   1.375 +            StringBuffer sb = new StringBuffer(country);
   1.376 +            sb.append(finalChar);
   1.377 +            return getInstance(sb.toString(), defaultFractionDigits, numericCode);
   1.378 +        } else {
   1.379 +            // special cases
   1.380 +            if (tableEntry == INVALID_COUNTRY_ENTRY) {
   1.381 +                throw new IllegalArgumentException();
   1.382 +            }
   1.383 +            if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
   1.384 +                return null;
   1.385 +            } else {
   1.386 +                int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
   1.387 +                if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
   1.388 +                    return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
   1.389 +                        scOldCurrenciesNumericCode[index]);
   1.390 +                } else {
   1.391 +                    return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
   1.392 +                        scNewCurrenciesNumericCode[index]);
   1.393 +                }
   1.394 +            }
   1.395 +        }
   1.396 +    }
   1.397 +
   1.398 +    /**
   1.399 +     * Gets the set of available currencies.  The returned set of currencies
   1.400 +     * contains all of the available currencies, which may include currencies
   1.401 +     * that represent obsolete ISO 4217 codes.  The set can be modified
   1.402 +     * without affecting the available currencies in the runtime.
   1.403 +     *
   1.404 +     * @return the set of available currencies.  If there is no currency
   1.405 +     *    available in the runtime, the returned set is empty.
   1.406 +     * @since 1.7
   1.407 +     */
   1.408 +    public static Set<Currency> getAvailableCurrencies() {
   1.409 +        synchronized(Currency.class) {
   1.410 +            if (available == null) {
   1.411 +                available = new HashSet<Currency>(256);
   1.412 +
   1.413 +                // Add simple currencies first
   1.414 +                for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
   1.415 +                    for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
   1.416 +                        int tableEntry = getMainTableEntry(c1, c2);
   1.417 +                        if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
   1.418 +                             && tableEntry != INVALID_COUNTRY_ENTRY) {
   1.419 +                            char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
   1.420 +                            int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
   1.421 +                            int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
   1.422 +                            StringBuilder sb = new StringBuilder();
   1.423 +                            sb.append(c1);
   1.424 +                            sb.append(c2);
   1.425 +                            sb.append(finalChar);
   1.426 +                            available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
   1.427 +                        }
   1.428 +                    }
   1.429 +                }
   1.430 +
   1.431 +                // Now add other currencies
   1.432 +                StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
   1.433 +                while (st.hasMoreElements()) {
   1.434 +                    available.add(getInstance((String)st.nextElement()));
   1.435 +                }
   1.436 +            }
   1.437 +        }
   1.438 +
   1.439 +        return (Set<Currency>) available.clone();
   1.440 +    }
   1.441 +
   1.442 +    /**
   1.443 +     * Gets the ISO 4217 currency code of this currency.
   1.444 +     *
   1.445 +     * @return the ISO 4217 currency code of this currency.
   1.446 +     */
   1.447 +    public String getCurrencyCode() {
   1.448 +        return currencyCode;
   1.449 +    }
   1.450 +
   1.451 +    /**
   1.452 +     * Gets the symbol of this currency for the default locale.
   1.453 +     * For example, for the US Dollar, the symbol is "$" if the default
   1.454 +     * locale is the US, while for other locales it may be "US$". If no
   1.455 +     * symbol can be determined, the ISO 4217 currency code is returned.
   1.456 +     *
   1.457 +     * @return the symbol of this currency for the default locale
   1.458 +     */
   1.459 +    public String getSymbol() {
   1.460 +        return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
   1.461 +    }
   1.462 +
   1.463 +    /**
   1.464 +     * Gets the symbol of this currency for the specified locale.
   1.465 +     * For example, for the US Dollar, the symbol is "$" if the specified
   1.466 +     * locale is the US, while for other locales it may be "US$". If no
   1.467 +     * symbol can be determined, the ISO 4217 currency code is returned.
   1.468 +     *
   1.469 +     * @param locale the locale for which a display name for this currency is
   1.470 +     * needed
   1.471 +     * @return the symbol of this currency for the specified locale
   1.472 +     * @exception NullPointerException if <code>locale</code> is null
   1.473 +     */
   1.474 +    public String getSymbol(Locale locale) {
   1.475 +        try {
   1.476 +            // Check whether a provider can provide an implementation that's closer
   1.477 +            // to the requested locale than what the Java runtime itself can provide.
   1.478 +            LocaleServiceProviderPool pool =
   1.479 +                LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
   1.480 +
   1.481 +            if (pool.hasProviders()) {
   1.482 +                // Assuming that all the country locales include necessary currency
   1.483 +                // symbols in the Java runtime's resources,  so there is no need to
   1.484 +                // examine whether Java runtime's currency resource bundle is missing
   1.485 +                // names.  Therefore, no resource bundle is provided for calling this
   1.486 +                // method.
   1.487 +                String symbol = pool.getLocalizedObject(
   1.488 +                                    CurrencyNameGetter.INSTANCE,
   1.489 +                                    locale, (OpenListResourceBundle)null,
   1.490 +                                    currencyCode, SYMBOL);
   1.491 +                if (symbol != null) {
   1.492 +                    return symbol;
   1.493 +                }
   1.494 +            }
   1.495 +
   1.496 +            ResourceBundle bundle = LocaleData.getCurrencyNames(locale);
   1.497 +            return bundle.getString(currencyCode);
   1.498 +        } catch (MissingResourceException e) {
   1.499 +            // use currency code as symbol of last resort
   1.500 +            return currencyCode;
   1.501 +        }
   1.502 +    }
   1.503 +
   1.504 +    /**
   1.505 +     * Gets the default number of fraction digits used with this currency.
   1.506 +     * For example, the default number of fraction digits for the Euro is 2,
   1.507 +     * while for the Japanese Yen it's 0.
   1.508 +     * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
   1.509 +     * -1 is returned.
   1.510 +     *
   1.511 +     * @return the default number of fraction digits used with this currency
   1.512 +     */
   1.513 +    public int getDefaultFractionDigits() {
   1.514 +        return defaultFractionDigits;
   1.515 +    }
   1.516 +
   1.517 +    /**
   1.518 +     * Returns the ISO 4217 numeric code of this currency.
   1.519 +     *
   1.520 +     * @return the ISO 4217 numeric code of this currency
   1.521 +     * @since 1.7
   1.522 +     */
   1.523 +    public int getNumericCode() {
   1.524 +        return numericCode;
   1.525 +    }
   1.526 +
   1.527 +    /**
   1.528 +     * Gets the name that is suitable for displaying this currency for
   1.529 +     * the default locale.  If there is no suitable display name found
   1.530 +     * for the default locale, the ISO 4217 currency code is returned.
   1.531 +     *
   1.532 +     * @return the display name of this currency for the default locale
   1.533 +     * @since 1.7
   1.534 +     */
   1.535 +    public String getDisplayName() {
   1.536 +        return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
   1.537 +    }
   1.538 +
   1.539 +    /**
   1.540 +     * Gets the name that is suitable for displaying this currency for
   1.541 +     * the specified locale.  If there is no suitable display name found
   1.542 +     * for the specified locale, the ISO 4217 currency code is returned.
   1.543 +     *
   1.544 +     * @param locale the locale for which a display name for this currency is
   1.545 +     * needed
   1.546 +     * @return the display name of this currency for the specified locale
   1.547 +     * @exception NullPointerException if <code>locale</code> is null
   1.548 +     * @since 1.7
   1.549 +     */
   1.550 +    public String getDisplayName(Locale locale) {
   1.551 +        try {
   1.552 +            OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
   1.553 +            String result = null;
   1.554 +            String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
   1.555 +
   1.556 +            // Check whether a provider can provide an implementation that's closer
   1.557 +            // to the requested locale than what the Java runtime itself can provide.
   1.558 +            LocaleServiceProviderPool pool =
   1.559 +                LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
   1.560 +            if (pool.hasProviders()) {
   1.561 +                result = pool.getLocalizedObject(
   1.562 +                                    CurrencyNameGetter.INSTANCE,
   1.563 +                                    locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
   1.564 +            }
   1.565 +
   1.566 +            if (result == null) {
   1.567 +                result = bundle.getString(bundleKey);
   1.568 +            }
   1.569 +
   1.570 +            if (result != null) {
   1.571 +                return result;
   1.572 +            }
   1.573 +        } catch (MissingResourceException e) {
   1.574 +            // fall through
   1.575 +        }
   1.576 +
   1.577 +        // use currency code as symbol of last resort
   1.578 +        return currencyCode;
   1.579 +    }
   1.580 +
   1.581 +    /**
   1.582 +     * Returns the ISO 4217 currency code of this currency.
   1.583 +     *
   1.584 +     * @return the ISO 4217 currency code of this currency
   1.585 +     */
   1.586 +    public String toString() {
   1.587 +        return currencyCode;
   1.588 +    }
   1.589 +
   1.590 +    /**
   1.591 +     * Resolves instances being deserialized to a single instance per currency.
   1.592 +     */
   1.593 +    private Object readResolve() {
   1.594 +        return getInstance(currencyCode);
   1.595 +    }
   1.596 +
   1.597 +    /**
   1.598 +     * Gets the main table entry for the country whose country code consists
   1.599 +     * of char1 and char2.
   1.600 +     */
   1.601 +    private static int getMainTableEntry(char char1, char char2) {
   1.602 +        if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
   1.603 +            throw new IllegalArgumentException();
   1.604 +        }
   1.605 +        return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
   1.606 +    }
   1.607 +
   1.608 +    /**
   1.609 +     * Sets the main table entry for the country whose country code consists
   1.610 +     * of char1 and char2.
   1.611 +     */
   1.612 +    private static void setMainTableEntry(char char1, char char2, int entry) {
   1.613 +        if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
   1.614 +            throw new IllegalArgumentException();
   1.615 +        }
   1.616 +        mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
   1.617 +    }
   1.618 +
   1.619 +    /**
   1.620 +     * Obtains a localized currency names from a CurrencyNameProvider
   1.621 +     * implementation.
   1.622 +     */
   1.623 +    private static class CurrencyNameGetter
   1.624 +        implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
   1.625 +                                                                   String> {
   1.626 +        private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
   1.627 +
   1.628 +        public String getObject(CurrencyNameProvider currencyNameProvider,
   1.629 +                                Locale locale,
   1.630 +                                String key,
   1.631 +                                Object... params) {
   1.632 +            assert params.length == 1;
   1.633 +            int type = (Integer)params[0];
   1.634 +
   1.635 +            switch(type) {
   1.636 +            case SYMBOL:
   1.637 +                return currencyNameProvider.getSymbol(key, locale);
   1.638 +            case DISPLAYNAME:
   1.639 +                return currencyNameProvider.getDisplayName(key, locale);
   1.640 +            default:
   1.641 +                assert false; // shouldn't happen
   1.642 +            }
   1.643 +
   1.644 +            return null;
   1.645 +        }
   1.646 +    }
   1.647 +
   1.648 +    private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
   1.649 +        int[] ret = new int[count];
   1.650 +        for (int i = 0; i < count; i++) {
   1.651 +            ret[i] = dis.readInt();
   1.652 +        }
   1.653 +
   1.654 +        return ret;
   1.655 +    }
   1.656 +
   1.657 +    private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
   1.658 +        long[] ret = new long[count];
   1.659 +        for (int i = 0; i < count; i++) {
   1.660 +            ret[i] = dis.readLong();
   1.661 +        }
   1.662 +
   1.663 +        return ret;
   1.664 +    }
   1.665 +
   1.666 +    private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
   1.667 +        String[] ret = new String[count];
   1.668 +        for (int i = 0; i < count; i++) {
   1.669 +            ret[i] = dis.readUTF();
   1.670 +        }
   1.671 +
   1.672 +        return ret;
   1.673 +    }
   1.674 +
   1.675 +    /**
   1.676 +     * Replaces currency data found in the currencydata.properties file
   1.677 +     *
   1.678 +     * @param pattern regex pattern for the properties
   1.679 +     * @param ctry country code
   1.680 +     * @param data currency data.  This is a comma separated string that
   1.681 +     *    consists of "three-letter alphabet code", "three-digit numeric code",
   1.682 +     *    and "one-digit (0,1,2, or 3) default fraction digit".
   1.683 +     *    For example, "JPZ,392,0".
   1.684 +     * @throws
   1.685 +     */
   1.686 +    private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
   1.687 +
   1.688 +        if (ctry.length() != 2) {
   1.689 +            // ignore invalid country code
   1.690 +            String message = new StringBuilder()
   1.691 +                .append("The entry in currency.properties for ")
   1.692 +                .append(ctry).append(" is ignored because of the invalid country code.")
   1.693 +                .toString();
   1.694 +            info(message, null);
   1.695 +            return;
   1.696 +        }
   1.697 +
   1.698 +        Matcher m = pattern.matcher(curdata);
   1.699 +        if (!m.find()) {
   1.700 +            // format is not recognized.  ignore the data
   1.701 +            String message = new StringBuilder()
   1.702 +                .append("The entry in currency.properties for ")
   1.703 +                .append(ctry)
   1.704 +                .append(" is ignored because the value format is not recognized.")
   1.705 +                .toString();
   1.706 +            info(message, null);
   1.707 +            return;
   1.708 +        }
   1.709 +
   1.710 +        String code = m.group(1);
   1.711 +        int numeric = Integer.parseInt(m.group(2));
   1.712 +        int fraction = Integer.parseInt(m.group(3));
   1.713 +        int entry = numeric << NUMERIC_CODE_SHIFT;
   1.714 +
   1.715 +        int index;
   1.716 +        for (index = 0; index < scOldCurrencies.length; index++) {
   1.717 +            if (scOldCurrencies[index].equals(code)) {
   1.718 +                break;
   1.719 +            }
   1.720 +        }
   1.721 +
   1.722 +        if (index == scOldCurrencies.length) {
   1.723 +            // simple case
   1.724 +            entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
   1.725 +                     (code.charAt(2) - 'A');
   1.726 +        } else {
   1.727 +            // special case
   1.728 +            entry |= SPECIAL_CASE_COUNTRY_MASK |
   1.729 +                     (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
   1.730 +        }
   1.731 +        setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
   1.732 +    }
   1.733 +
   1.734 +    private static void info(String message, Throwable t) {
   1.735 +        PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
   1.736 +        if (logger.isLoggable(PlatformLogger.INFO)) {
   1.737 +            if (t != null) {
   1.738 +                logger.info(message, t);
   1.739 +            } else {
   1.740 +                logger.info(message);
   1.741 +            }
   1.742 +        }
   1.743 +    }
   1.744 +}