Currency.java revision 6975f84c2ed72e1e26d20190b6f318718c849008
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.util;
28
29import java.io.Serializable;
30import java.util.concurrent.ConcurrentHashMap;
31import java.util.concurrent.ConcurrentMap;
32
33import libcore.icu.ICU;
34
35/**
36 * Represents a currency. Currencies are identified by their ISO 4217 currency
37 * codes. Visit the <a href="http://www.iso.org/iso/home/standards/currency_codes.htm">
38 * ISO web site</a> for more information.
39 * <p>
40 * The class is designed so that there's never more than one
41 * <code>Currency</code> instance for any given currency. Therefore, there's
42 * no public constructor. You obtain a <code>Currency</code> instance using
43 * the <code>getInstance</code> methods.
44 *
45 * @since 1.4
46 */
47// Android-changed: Superseding runtime currency data via a properties file is not
48// supported on Android.
49public final class Currency implements Serializable {
50
51    private static final long serialVersionUID = -158308464356906721L;
52
53    /**
54     * ISO 4217 currency code for this currency.
55     *
56     * @serial
57     */
58    private final String currencyCode;
59
60    // class data: instance map
61
62    private static ConcurrentMap<String, Currency> instances = new ConcurrentHashMap<>(7);
63    private static HashSet<Currency> available;
64
65    // Android-changed: Implement Currency on top of ICU throughout.
66    // We do not keep track of defaultFractionDigits and numericCode separately here.
67    private transient final android.icu.util.Currency icuCurrency;
68
69    /**
70     * Constructs a <code>Currency</code> instance. The constructor is private
71     * so that we can insure that there's never more than one instance for a
72     * given currency.
73     */
74    private Currency(android.icu.util.Currency icuCurrency) {
75        this.icuCurrency = icuCurrency;
76        this.currencyCode = icuCurrency.getCurrencyCode();
77    }
78
79    /**
80     * Returns the <code>Currency</code> instance for the given currency code.
81     *
82     * @param currencyCode the ISO 4217 code of the currency
83     * @return the <code>Currency</code> instance for the given currency code
84     * @exception NullPointerException if <code>currencyCode</code> is null
85     * @exception IllegalArgumentException if <code>currencyCode</code> is not
86     * a supported ISO 4217 code.
87     */
88    public static Currency getInstance(String currencyCode) {
89        // BEGIN Android-changed: use ICU
90        Currency instance = instances.get(currencyCode);
91        if (instance != null) {
92            return instance;
93        }
94        android.icu.util.Currency icuInstance =
95                  android.icu.util.Currency.getInstance(currencyCode);
96        if (icuInstance == null) {
97            return null;
98        }
99        Currency currencyVal = new Currency(icuInstance);
100        // END Android-changed
101        instance = instances.putIfAbsent(currencyCode, currencyVal);
102        return (instance != null ? instance : currencyVal);
103    }
104
105    /**
106     * Returns the <code>Currency</code> instance for the country of the
107     * given locale. The language and variant components of the locale
108     * are ignored. The result may vary over time, as countries change their
109     * currencies. For example, for the original member countries of the
110     * European Monetary Union, the method returns the old national currencies
111     * until December 31, 2001, and the Euro from January 1, 2002, local time
112     * of the respective countries.
113     * <p>
114     * The method returns <code>null</code> for territories that don't
115     * have a currency, such as Antarctica.
116     *
117     * @param locale the locale for whose country a <code>Currency</code>
118     * instance is needed
119     * @return the <code>Currency</code> instance for the country of the given
120     * locale, or {@code null}
121     * @exception NullPointerException if <code>locale</code> or its country
122     * code is {@code null}
123     * @exception IllegalArgumentException if the country of the given {@code locale}
124     * is not a supported ISO 3166 country code.
125     */
126    public static Currency getInstance(Locale locale) {
127        // BEGIN Android-changed: use ICU
128        android.icu.util.Currency icuInstance =
129                android.icu.util.Currency.getInstance(locale);
130        String variant = locale.getVariant();
131        String country = locale.getCountry();
132        if (!variant.isEmpty() && (variant.equals("EURO") || variant.equals("HK") ||
133                variant.equals("PREEURO"))) {
134            country = country + "_" + variant;
135        }
136        String currencyCode = ICU.getCurrencyCode(country);
137        if (currencyCode == null) {
138            throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
139        }
140        if (icuInstance == null || icuInstance.getCurrencyCode().equals("XXX")) {
141            return null;
142        }
143        return getInstance(currencyCode);
144        // END Android-changed
145    }
146
147    /**
148     * Gets the set of available currencies.  The returned set of currencies
149     * contains all of the available currencies, which may include currencies
150     * that represent obsolete ISO 4217 codes.  The set can be modified
151     * without affecting the available currencies in the runtime.
152     *
153     * @return the set of available currencies.  If there is no currency
154     *    available in the runtime, the returned set is empty.
155     * @since 1.7
156     */
157    public static Set<Currency> getAvailableCurrencies() {
158        synchronized(Currency.class) {
159            if (available == null) {
160                // BEGIN Android-changed: use ICU
161                Set<android.icu.util.Currency> icuAvailableCurrencies
162                        = android.icu.util.Currency.getAvailableCurrencies();
163                available = new HashSet<>();
164                for (android.icu.util.Currency icuCurrency : icuAvailableCurrencies) {
165                    Currency currency = getInstance(icuCurrency.getCurrencyCode());
166                    if (currency == null) {
167                        currency = new Currency(icuCurrency);
168                        instances.put(currency.currencyCode, currency);
169                    }
170                    available.add(currency);
171                }
172                // END Android-changed
173            }
174        }
175
176        @SuppressWarnings("unchecked")
177        Set<Currency> result = (Set<Currency>) available.clone();
178        return result;
179    }
180
181    /**
182     * Gets the ISO 4217 currency code of this currency.
183     *
184     * @return the ISO 4217 currency code of this currency.
185     */
186    public String getCurrencyCode() {
187        return currencyCode;
188    }
189
190    /**
191     * Gets the symbol of this currency for the default
192     * {@link Locale.Category#DISPLAY DISPLAY} locale.
193     * For example, for the US Dollar, the symbol is "$" if the default
194     * locale is the US, while for other locales it may be "US$". If no
195     * symbol can be determined, the ISO 4217 currency code is returned.
196     * <p>
197     * This is equivalent to calling
198     * {@link #getSymbol(Locale)
199     *     getSymbol(Locale.getDefault(Locale.Category.DISPLAY))}.
200     *
201     * @return the symbol of this currency for the default
202     *     {@link Locale.Category#DISPLAY DISPLAY} locale
203     */
204    public String getSymbol() {
205        return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
206    }
207
208    /**
209     * Gets the symbol of this currency for the specified locale.
210     * For example, for the US Dollar, the symbol is "$" if the specified
211     * locale is the US, while for other locales it may be "US$". If no
212     * symbol can be determined, the ISO 4217 currency code is returned.
213     *
214     * @param locale the locale for which a display name for this currency is
215     * needed
216     * @return the symbol of this currency for the specified locale
217     * @exception NullPointerException if <code>locale</code> is null
218     */
219    public String getSymbol(Locale locale) {
220        // BEGIN Android-changed: use ICU
221        if (locale == null) {
222            throw new NullPointerException("locale == null");
223        }
224        return icuCurrency.getSymbol(locale);
225        // END Android-changed
226    }
227
228    /**
229     * Gets the default number of fraction digits used with this currency.
230     * For example, the default number of fraction digits for the Euro is 2,
231     * while for the Japanese Yen it's 0.
232     * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
233     * -1 is returned.
234     *
235     * @return the default number of fraction digits used with this currency
236     */
237    public int getDefaultFractionDigits() {
238        // BEGIN Android-changed: use ICU
239        if (icuCurrency.getCurrencyCode().equals("XXX")) {
240            return -1;
241        }
242        return icuCurrency.getDefaultFractionDigits();
243        // END Android-changed
244    }
245
246    /**
247     * Returns the ISO 4217 numeric code of this currency.
248     *
249     * @return the ISO 4217 numeric code of this currency
250     * @since 1.7
251     */
252    public int getNumericCode() {
253        // Android-changed: use ICU
254        // was: return numericCode;
255        return icuCurrency.getNumericCode();
256    }
257
258    /**
259     * Gets the name that is suitable for displaying this currency for
260     * the default {@link Locale.Category#DISPLAY DISPLAY} locale.
261     * If there is no suitable display name found
262     * for the default locale, the ISO 4217 currency code is returned.
263     * <p>
264     * This is equivalent to calling
265     * {@link #getDisplayName(Locale)
266     *     getDisplayName(Locale.getDefault(Locale.Category.DISPLAY))}.
267     *
268     * @return the display name of this currency for the default
269     *     {@link Locale.Category#DISPLAY DISPLAY} locale
270     * @since 1.7
271     */
272    public String getDisplayName() {
273        return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
274    }
275
276    /**
277     * Gets the name that is suitable for displaying this currency for
278     * the specified locale.  If there is no suitable display name found
279     * for the specified locale, the ISO 4217 currency code is returned.
280     *
281     * @param locale the locale for which a display name for this currency is
282     * needed
283     * @return the display name of this currency for the specified locale
284     * @exception NullPointerException if <code>locale</code> is null
285     * @since 1.7
286     */
287    public String getDisplayName(Locale locale) {
288        // Android-changed: use ICU
289        return icuCurrency.getDisplayName(Objects.requireNonNull(locale));
290    }
291
292    /**
293     * Returns the ISO 4217 currency code of this currency.
294     *
295     * @return the ISO 4217 currency code of this currency
296     */
297    @Override
298    public String toString() {
299        // Android-changed: use ICU
300        return icuCurrency.toString();
301    }
302
303    /**
304     * Resolves instances being deserialized to a single instance per currency.
305     */
306    private Object readResolve() {
307        return getInstance(currencyCode);
308    }
309}
310