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      */
     95     public static Set<Currency> getAvailableCurrencies() {
     96         Set<Currency> result = new LinkedHashSet<Currency>();
     97         String[] currencyCodes = ICU.getAvailableCurrencyCodes();
     98         for (String currencyCode : currencyCodes) {
     99             result.add(Currency.getInstance(currencyCode));
    100         }
    101         return result;
    102     }
    103 
    104     /**
    105      * Returns this currency's ISO 4217 currency code.
    106      */
    107     public String getCurrencyCode() {
    108         return currencyCode;
    109     }
    110 
    111     /**
    112      * Equivalent to {@code getDisplayName(Locale.getDefault())}.
    113      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    114      * @since 1.7
    115      */
    116     public String getDisplayName() {
    117         return getDisplayName(Locale.getDefault());
    118     }
    119 
    120     /**
    121      * Returns the localized name of this currency in the given {@code locale}.
    122      * Returns the ISO 4217 currency code if no localized name is available.
    123      * @since 1.7
    124      */
    125     public String getDisplayName(Locale locale) {
    126         return ICU.getCurrencyDisplayName(locale.toString(), currencyCode);
    127     }
    128 
    129     /**
    130      * Equivalent to {@code getSymbol(Locale.getDefault())}.
    131      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    132      */
    133     public String getSymbol() {
    134         return getSymbol(Locale.getDefault());
    135     }
    136 
    137     /**
    138      * Returns the localized currency symbol for this currency in {@code locale}.
    139      * That is, given "USD" and Locale.US, you'd get "$", but given "USD" and a non-US locale,
    140      * you'd get "US$".
    141      *
    142      * <p>If the locale only specifies a language rather than a language and a country (such as
    143      * {@code Locale.JAPANESE} or {new Locale("en", "")} rather than {@code Locale.JAPAN} or
    144      * {new Locale("en", "US")}), the ISO 4217 currency code is returned.
    145      *
    146      * <p>If there is no locale-specific currency symbol, the ISO 4217 currency code is returned.
    147      */
    148     public String getSymbol(Locale locale) {
    149         if (locale.getCountry().length() == 0) {
    150             return currencyCode;
    151         }
    152 
    153         // Check the locale first, in case the locale has the same currency.
    154         LocaleData localeData = LocaleData.get(locale);
    155         if (localeData.internationalCurrencySymbol.equals(currencyCode)) {
    156             return localeData.currencySymbol;
    157         }
    158 
    159         // Try ICU, and fall back to the currency code if ICU has nothing.
    160         String symbol = ICU.getCurrencySymbol(locale.toString(), currencyCode);
    161         return symbol != null ? symbol : currencyCode;
    162     }
    163 
    164     /**
    165      * Returns the default number of fraction digits for this currency.
    166      * For instance, the default number of fraction digits for the US dollar is 2 because there are
    167      * 100 US cents in a US dollar. For the Japanese Yen, the number is 0 because coins smaller
    168      * than 1 Yen became invalid in 1953. In the case of pseudo-currencies, such as
    169      * IMF Special Drawing Rights, -1 is returned.
    170      */
    171     public int getDefaultFractionDigits() {
    172         // In some places the code XXX is used as the fall back currency.
    173         // The RI returns -1, but ICU defaults to 2 for unknown currencies.
    174         if (currencyCode.equals("XXX")) {
    175             return -1;
    176         }
    177         return ICU.getCurrencyFractionDigits(currencyCode);
    178     }
    179 
    180     /**
    181      * Returns this currency's ISO 4217 currency code.
    182      */
    183     @Override
    184     public String toString() {
    185         return currencyCode;
    186     }
    187 
    188     private Object readResolve() {
    189         return getInstance(currencyCode);
    190     }
    191 }
    192