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, 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             if (locale == null) {
     69                 throw new NullPointerException("locale == null");
     70             }
     71             Currency currency = localesToCurrencies.get(locale);
     72             if (currency != null) {
     73                 return currency;
     74             }
     75             String country = locale.getCountry();
     76             String variant = locale.getVariant();
     77             if (!variant.isEmpty() && (variant.equals("EURO") || variant.equals("HK") ||
     78                     variant.equals("PREEURO"))) {
     79                 country = country + "_" + variant;
     80             }
     81 
     82             String currencyCode = ICU.getCurrencyCode(country);
     83             if (currencyCode == null) {
     84                 throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
     85             } else if (currencyCode.equals("XXX")) {
     86                 return null;
     87             }
     88             Currency result = getInstance(currencyCode);
     89             localesToCurrencies.put(locale, result);
     90             return result;
     91         }
     92     }
     93 
     94     /**
     95      * Returns a set of all known currencies.
     96      * @since 1.7
     97      */
     98     public static Set<Currency> getAvailableCurrencies() {
     99         Set<Currency> result = new LinkedHashSet<Currency>();
    100         String[] currencyCodes = ICU.getAvailableCurrencyCodes();
    101         for (String currencyCode : currencyCodes) {
    102             result.add(Currency.getInstance(currencyCode));
    103         }
    104         return result;
    105     }
    106 
    107     /**
    108      * Returns this currency's ISO 4217 currency code.
    109      */
    110     public String getCurrencyCode() {
    111         return currencyCode;
    112     }
    113 
    114     /**
    115      * Equivalent to {@code getDisplayName(Locale.getDefault())}.
    116      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    117      * @since 1.7
    118      */
    119     public String getDisplayName() {
    120         return getDisplayName(Locale.getDefault());
    121     }
    122 
    123     /**
    124      * Returns the localized name of this currency in the given {@code locale}.
    125      * Returns the ISO 4217 currency code if no localized name is available.
    126      * @since 1.7
    127      */
    128     public String getDisplayName(Locale locale) {
    129         return ICU.getCurrencyDisplayName(locale, 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 == null) {
    153             throw new NullPointerException("locale == null");
    154         }
    155         // Check the locale first, in case the locale has the same currency.
    156         LocaleData localeData = LocaleData.get(locale);
    157         if (localeData.internationalCurrencySymbol.equals(currencyCode)) {
    158             return localeData.currencySymbol;
    159         }
    160 
    161         // Try ICU, and fall back to the currency code if ICU has nothing.
    162         String symbol = ICU.getCurrencySymbol(locale, currencyCode);
    163         return symbol != null ? symbol : currencyCode;
    164     }
    165 
    166     /**
    167      * Returns the default number of fraction digits for this currency.
    168      * For instance, the default number of fraction digits for the US dollar is 2 because there are
    169      * 100 US cents in a US dollar. For the Japanese Yen, the number is 0 because coins smaller
    170      * than 1 Yen became invalid in 1953. In the case of pseudo-currencies, such as
    171      * IMF Special Drawing Rights, -1 is returned.
    172      */
    173     public int getDefaultFractionDigits() {
    174         // In some places the code XXX is used as the fall back currency.
    175         // The RI returns -1, but ICU defaults to 2 for unknown currencies.
    176         if (currencyCode.equals("XXX")) {
    177             return -1;
    178         }
    179         return ICU.getCurrencyFractionDigits(currencyCode);
    180     }
    181 
    182     /**
    183      * Returns this currency's ISO 4217 currency code.
    184      */
    185     @Override
    186     public String toString() {
    187         return currencyCode;
    188     }
    189 
    190     private Object readResolve() {
    191         return getInstance(currencyCode);
    192     }
    193 }
    194