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