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 18package java.util; 19 20// BEGIN android-added 21import com.ibm.icu4jni.util.LocaleData; 22import com.ibm.icu4jni.util.Resources; 23import java.util.logging.Logger; 24import org.apache.harmony.luni.util.Msg; 25// END android-added 26 27import java.security.AccessController; 28import java.io.Serializable; 29import java.security.PrivilegedAction; 30 31/** 32 * This class represents a currency as identified in the ISO 4217 currency 33 * codes. 34 */ 35public final class Currency implements Serializable { 36 37 private static final long serialVersionUID = -158308464356906721L; 38 39 private static final Hashtable<String, Currency> codesToCurrencies = new Hashtable<String, Currency>(); 40 private static final Hashtable<Locale, Currency> localesToCurrencies = new Hashtable<Locale, Currency>(); 41 42 private final String currencyCode; 43 44 // BEGIN android-added 45 // TODO: this isn't set if we're restored from serialized form, 46 // so getDefaultFractionDigits always returns 0! 47 private transient int defaultFractionDigits; 48 // END android-added 49 50 private Currency(String currencyCode) { 51 // BEGIN android-changed 52 this.currencyCode = currencyCode; 53 54 // In some places the code XXX is used as the fall back currency. 55 // The RI returns -1, but ICU defaults to 2 for unknown currencies. 56 if (currencyCode.equals("XXX")) { 57 this.defaultFractionDigits = -1; 58 return; 59 } 60 61 this.defaultFractionDigits = Resources.getCurrencyFractionDigitsNative(currencyCode); 62 if (defaultFractionDigits < 0) { 63 throw new IllegalArgumentException(Msg.getString("K0322", currencyCode)); 64 } 65 // END android-changed 66 } 67 68 /** 69 * Returns the {@code Currency} instance for the given currency code. 70 * <p> 71 * 72 * @param currencyCode 73 * the currency code. 74 * @return the {@code Currency} instance for this currency code. 75 * 76 * @throws IllegalArgumentException 77 * if the currency code is not a supported ISO 4217 currency 78 * code. 79 */ 80 public static Currency getInstance(String currencyCode) { 81 // BEGIN android-changed 82 Currency currency = codesToCurrencies.get(currencyCode); 83 if (currency == null) { 84 currency = new Currency(currencyCode); 85 codesToCurrencies.put(currencyCode, currency); 86 } 87 return currency; 88 // END android-changed 89 } 90 91 /** 92 * Returns the {@code Currency} instance for this {@code Locale}'s country. 93 * 94 * @param locale 95 * the {@code Locale} of a country. 96 * @return the {@code Currency} used in the country defined by the locale parameter. 97 * 98 * @throws IllegalArgumentException 99 * if the locale's country is not a supported ISO 3166 Country. 100 */ 101 public static Currency getInstance(Locale locale) { 102 // BEGIN android-changed 103 Currency currency = localesToCurrencies.get(locale); 104 if (currency != null) { 105 return currency; 106 } 107 String country = locale.getCountry(); 108 String variant = locale.getVariant(); 109 if (variant.length() > 0 && (variant.equals("EURO") || variant.equals("HK") || 110 variant.equals("PREEURO"))) { 111 country = country + "_" + variant; 112 } 113 114 String currencyCode = Resources.getCurrencyCodeNative(country); 115 if (currencyCode == null) { 116 throw new IllegalArgumentException(Msg.getString("K0323", locale.toString())); 117 } else if (currencyCode.equals("None")) { 118 return null; 119 } 120 Currency result = getInstance(currencyCode); 121 localesToCurrencies.put(locale, result); 122 return result; 123 // END android-changed 124 } 125 126 /** 127 * Returns this {@code Currency}'s ISO 4217 currency code. 128 * 129 * @return this {@code Currency}'s ISO 4217 currency code. 130 */ 131 public String getCurrencyCode() { 132 return currencyCode; 133 } 134 135 /** 136 * Returns the symbol for this currency in the default locale. For instance, 137 * if the default locale is the US, the symbol of the US dollar is "$". For 138 * other locales it may be "US$". If no symbol can be determined, the ISO 139 * 4217 currency code of the US dollar is returned. 140 * 141 * @return the symbol for this {@code Currency} in the default {@code Locale}. 142 */ 143 public String getSymbol() { 144 return getSymbol(Locale.getDefault()); 145 } 146 147 /** 148 * Returns the symbol for this currency in the given {@code Locale}. 149 * That is, given "USD" and Locale.US, you'd get "$", but given "USD" and a non-US locale, 150 * you'd get "US$". 151 * <p> 152 * If the locale only specifies a language rather than a language and a countries (e.g. 153 * {@code Locale.JAPANESE, new Locale("en","")}), the the ISO 154 * 4217 currency code is returned. 155 * <p> 156 * If there is no currency symbol specific to this locale does not exist, the 157 * ISO 4217 currency code is returned. 158 * <p> 159 * 160 * @param locale 161 * the locale for which the currency symbol should be returned. 162 * @return the representation of this {@code Currency}'s symbol in the specified 163 * locale. 164 */ 165 public String getSymbol(Locale locale) { 166 // BEGIN android-changed 167 if (locale.getCountry().length() == 0) { 168 return currencyCode; 169 } 170 171 // Check the locale first, in case the locale has the same currency. 172 LocaleData localeData = Resources.getLocaleData(locale); 173 if (localeData.internationalCurrencySymbol.equals(currencyCode)) { 174 return localeData.currencySymbol; 175 } 176 177 // Try ICU, and fall back to the currency code if ICU has nothing. 178 String symbol = Resources.getCurrencySymbolNative(locale.toString(), currencyCode); 179 return symbol != null ? symbol : currencyCode; 180 // END android-changed 181 } 182 183 /** 184 * Returns the default number of fraction digits for this currency. For 185 * instance, the default number of fraction digits for the US dollar is 2. 186 * For the Japanese Yen the number is 0. In the case of pseudo-currencies, 187 * such as IMF Special Drawing Rights, -1 is returned. 188 * 189 * @return the default number of fraction digits for this currency. 190 */ 191 public int getDefaultFractionDigits() { 192 // BEGIN android-changed 193 // return com.ibm.icu.util.Currency.getInstance(currencyCode).getDefaultFractionDigits(); 194 return defaultFractionDigits; 195 // END android-changed 196 } 197 198 /** 199 * Returns this currency's ISO 4217 currency code. 200 * 201 * @return this currency's ISO 4217 currency code. 202 */ 203 @Override 204 public String toString() { 205 return currencyCode; 206 } 207 208 private Object readResolve() { 209 return getInstance(currencyCode); 210 } 211 212 // TODO: remove this in favor of direct access (and no ResourceBundle cruft). 213 private static ResourceBundle getCurrencyBundle(final Locale locale) { 214 return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() { 215 public ResourceBundle run() { 216 String bundle = "org.apache.harmony.luni.internal.locale.Currency"; 217 return ResourceBundle.getBundle(bundle, locale); 218 } 219 }); 220 } 221} 222