Home | History | Annotate | Download | only in util
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.util;
     19 
     20 import java.io.Serializable;
     21 import libcore.icu.ICU;
     22 import libcore.icu.LocaleData;
     23 
     24 /**
     25  * A currency corresponding to an <a href="http://en.wikipedia.org/wiki/ISO_4217">ISO 4217</a>
     26  * currency code such as "EUR" or "USD".
     27  */
     28 public final class Currency implements Serializable {
     29     private static final long serialVersionUID = -158308464356906721L;
     30 
     31     private static final HashMap<String, Currency> codesToCurrencies = new HashMap<String, Currency>();
     32     private static final HashMap<Locale, Currency> localesToCurrencies = new HashMap<Locale, Currency>();
     33 
     34     private final String currencyCode;
     35 
     36     private Currency(String currencyCode) {
     37         this.currencyCode = currencyCode;
     38         String symbol = ICU.getCurrencySymbol(Locale.US.toString(), currencyCode);
     39         if (symbol == null) {
     40             throw new IllegalArgumentException("Unsupported ISO 4217 currency code: " +
     41                     currencyCode);
     42         }
     43     }
     44 
     45     /**
     46      * Returns the {@code Currency} instance for the given ISO 4217 currency code.
     47      * @throws IllegalArgumentException
     48      *             if the currency code is not a supported ISO 4217 currency code.
     49      */
     50     public static Currency getInstance(String currencyCode) {
     51         synchronized (codesToCurrencies) {
     52             Currency currency = codesToCurrencies.get(currencyCode);
     53             if (currency == null) {
     54                 currency = new Currency(currencyCode);
     55                 codesToCurrencies.put(currencyCode, currency);
     56             }
     57             return currency;
     58         }
     59     }
     60 
     61     /**
     62      * Returns the {@code Currency} instance for this {@code Locale}'s country.
     63      * @throws IllegalArgumentException
     64      *             if the locale's country is not a supported ISO 3166 country.
     65      */
     66     public static Currency getInstance(Locale locale) {
     67         synchronized (localesToCurrencies) {
     68             Currency currency = localesToCurrencies.get(locale);
     69             if (currency != null) {
     70                 return currency;
     71             }
     72             String country = locale.getCountry();
     73             String variant = locale.getVariant();
     74             if (!variant.isEmpty() && (variant.equals("EURO") || variant.equals("HK") ||
     75                     variant.equals("PREEURO"))) {
     76                 country = country + "_" + variant;
     77             }
     78 
     79             String currencyCode = ICU.getCurrencyCode(country);
     80             if (currencyCode == null) {
     81                 throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
     82             } else if (currencyCode.equals("XXX")) {
     83                 return null;
     84             }
     85             Currency result = getInstance(currencyCode);
     86             localesToCurrencies.put(locale, result);
     87             return result;
     88         }
     89     }
     90 
     91     /**
     92      * Returns a set of all known currencies.
     93      * @since 1.7
     94      * @hide 1.7
     95      */
     96     public static Set<Currency> getAvailableCurrencies() {
     97         Set<Currency> result = new LinkedHashSet<Currency>();
     98         String[] currencyCodes = ICU.getAvailableCurrencyCodes();
     99         for (String currencyCode : currencyCodes) {
    100             result.add(Currency.getInstance(currencyCode));
    101         }
    102         return result;
    103     }
    104 
    105     /**
    106      * Returns this currency's ISO 4217 currency code.
    107      */
    108     public String getCurrencyCode() {
    109         return currencyCode;
    110     }
    111 
    112     /**
    113      * Equivalent to {@code getDisplayName(Locale.getDefault())}.
    114      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    115      * @since 1.7
    116      * @hide 1.7
    117      */
    118     public String getDisplayName() {
    119         return getDisplayName(Locale.getDefault());
    120     }
    121 
    122     /**
    123      * Returns the localized name of this currency in the given {@code locale}.
    124      * Returns the ISO 4217 currency code if no localized name is available.
    125      * @since 1.7
    126      * @hide 1.7
    127      */
    128     public String getDisplayName(Locale locale) {
    129         return ICU.getCurrencyDisplayName(locale.toString(), currencyCode);
    130     }
    131 
    132     /**
    133      * Equivalent to {@code getSymbol(Locale.getDefault())}.
    134      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    135      */
    136     public String getSymbol() {
    137         return getSymbol(Locale.getDefault());
    138     }
    139 
    140     /**
    141      * Returns the localized currency symbol for this currency in {@code locale}.
    142      * That is, given "USD" and Locale.US, you'd get "$", but given "USD" and a non-US locale,
    143      * you'd get "US$".
    144      *
    145      * <p>If the locale only specifies a language rather than a language and a country (such as
    146      * {@code Locale.JAPANESE} or {new Locale("en", "")} rather than {@code Locale.JAPAN} or
    147      * {new Locale("en", "US")}), the ISO 4217 currency code is returned.
    148      *
    149      * <p>If there is no locale-specific currency symbol, the ISO 4217 currency code is returned.
    150      */
    151     public String getSymbol(Locale locale) {
    152         if (locale.getCountry().length() == 0) {
    153             return currencyCode;
    154         }
    155 
    156         // Check the locale first, in case the locale has the same currency.
    157         LocaleData localeData = LocaleData.get(locale);
    158         if (localeData.internationalCurrencySymbol.equals(currencyCode)) {
    159             return localeData.currencySymbol;
    160         }
    161 
    162         // Try ICU, and fall back to the currency code if ICU has nothing.
    163         String symbol = ICU.getCurrencySymbol(locale.toString(), currencyCode);
    164         return symbol != null ? symbol : currencyCode;
    165     }
    166 
    167     /**
    168      * Returns the default number of fraction digits for this currency.
    169      * For instance, the default number of fraction digits for the US dollar is 2 because there are
    170      * 100 US cents in a US dollar. For the Japanese Yen, the number is 0 because coins smaller
    171      * than 1 Yen became invalid in 1953. In the case of pseudo-currencies, such as
    172      * IMF Special Drawing Rights, -1 is returned.
    173      */
    174     public int getDefaultFractionDigits() {
    175         // In some places the code XXX is used as the fall back currency.
    176         // The RI returns -1, but ICU defaults to 2 for unknown currencies.
    177         if (currencyCode.equals("XXX")) {
    178             return -1;
    179         }
    180         return ICU.getCurrencyFractionDigits(currencyCode);
    181     }
    182 
    183     /**
    184      * Returns this currency's ISO 4217 currency code.
    185      */
    186     @Override
    187     public String toString() {
    188         return currencyCode;
    189     }
    190 
    191     private Object readResolve() {
    192         return getInstance(currencyCode);
    193     }
    194 }
    195