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