Currency.java revision 7e3c9197dd6819e459d95e986cb028b61e708da1
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2011, 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;
30
31import libcore.icu.ICU;
32
33/**
34 * Represents a currency. Currencies are identified by their ISO 4217 currency
35 * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
36 * ISO web site</a> for more information, including a table of
37 * currency codes.
38 * <p>
39 * The class is designed so that there's never more than one
40 * <code>Currency</code> instance for any given currency. Therefore, there's
41 * no public constructor. You obtain a <code>Currency</code> instance using
42 * the <code>getInstance</code> methods.
43 * <p>
44 * Users can supersede the Java runtime currency data by creating a properties
45 * file named <code>&lt;JAVA_HOME&gt;/lib/currency.properties</code>.  The contents
46 * of the properties file are key/value pairs of the ISO 3166 country codes
47 * and the ISO 4217 currency data respectively.  The value part consists of
48 * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
49 * code, and a minor unit.  Those three ISO 4217 values are separated by commas.
50 * The lines which start with '#'s are considered comment lines.  For example,
51 * <p>
52 * <code>
53 * #Sample currency properties<br>
54 * JP=JPZ,999,0
55 * </code>
56 * <p>
57 * will supersede the currency data for Japan.
58 *
59 * @since 1.4
60 */
61public final class Currency implements Serializable {
62
63    private static final long serialVersionUID = -158308464356906721L;
64
65    private static HashMap<String, Currency> instances = new HashMap<>();
66
67    private static HashSet<Currency> available;
68
69    private transient final android.icu.util.Currency icuCurrency;
70
71    private final String currencyCode;
72
73    /**
74     * Constructs a <code>Currency</code> instance. The constructor is private
75     * so that we can insure that there's never more than one instance for a
76     * given currency.
77     */
78    private Currency(android.icu.util.Currency icuCurrency) {
79        this.icuCurrency = icuCurrency;
80        this.currencyCode = icuCurrency.getCurrencyCode();
81    }
82
83    /**
84     * Returns the <code>Currency</code> instance for the given currency code.
85     *
86     * @param currencyCode the ISO 4217 code of the currency
87     * @return the <code>Currency</code> instance for the given currency code
88     * @exception NullPointerException if <code>currencyCode</code> is null
89     * @exception IllegalArgumentException if <code>currencyCode</code> is not
90     * a supported ISO 4217 code.
91     */
92    public static Currency getInstance(String currencyCode) {
93        synchronized (instances) {
94            Currency instance = instances.get(currencyCode);
95            if (instance == null) {
96                android.icu.util.Currency icuInstance =
97                        android.icu.util.Currency.getInstance(currencyCode);
98                if (icuInstance == null) {
99                    return null;
100                }
101                instance = new Currency(icuInstance);
102                instances.put(currencyCode, instance);
103            }
104            return instance;
105        }
106    }
107
108    /**
109     * Returns the <code>Currency</code> instance for the country of the
110     * given locale. The language and variant components of the locale
111     * are ignored. The result may vary over time, as countries change their
112     * currencies. For example, for the original member countries of the
113     * European Monetary Union, the method returns the old national currencies
114     * until December 31, 2001, and the Euro from January 1, 2002, local time
115     * of the respective countries.
116     * <p>
117     * The method returns <code>null</code> for territories that don't
118     * have a currency, such as Antarctica.
119     *
120     * @param locale the locale for whose country a <code>Currency</code>
121     * instance is needed
122     * @return the <code>Currency</code> instance for the country of the given
123     * locale, or null
124     * @exception NullPointerException if <code>locale</code> or its country
125     * code is null
126     * @exception IllegalArgumentException if the country of the given locale
127     * is not a supported ISO 3166 country code.
128     */
129    public static Currency getInstance(Locale locale) {
130        android.icu.util.Currency icuInstance =
131                android.icu.util.Currency.getInstance(locale);
132        String variant = locale.getVariant();
133        String country = locale.getCountry();
134        if (!variant.isEmpty() && (variant.equals("EURO") || variant.equals("HK") ||
135                variant.equals("PREEURO"))) {
136            country = country + "_" + variant;
137        }
138        String currencyCode = ICU.getCurrencyCode(country);
139        if (currencyCode == null) {
140            throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
141        }
142        if (icuInstance == null || icuInstance.getCurrencyCode().equals("XXX")) {
143            return null;
144        }
145        return getInstance(currencyCode);
146    }
147
148    /**
149     * Gets the set of available currencies.  The returned set of currencies
150     * contains all of the available currencies, which may include currencies
151     * that represent obsolete ISO 4217 codes.  The set can be modified
152     * without affecting the available currencies in the runtime.
153     *
154     * @return the set of available currencies.  If there is no currency
155     *    available in the runtime, the returned set is empty.
156     * @since 1.7
157     */
158    public static Set<Currency> getAvailableCurrencies() {
159        synchronized (Currency.class) {
160            if (available == null) {
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            }
173            return (Set<Currency>) available.clone();
174        }
175    }
176
177    /**
178     * Gets the ISO 4217 currency code of this currency.
179     *
180     * @return the ISO 4217 currency code of this currency.
181     */
182    public String getCurrencyCode() {
183        return currencyCode;
184    }
185
186    /**
187     * Gets the symbol of this currency for the default locale.
188     * For example, for the US Dollar, the symbol is "$" if the default
189     * locale is the US, while for other locales it may be "US$". If no
190     * symbol can be determined, the ISO 4217 currency code is returned.
191     *
192     * @return the symbol of this currency for the default locale
193     */
194    public String getSymbol() {
195        return icuCurrency.getSymbol();
196    }
197
198    /**
199     * Gets the symbol of this currency for the specified locale.
200     * For example, for the US Dollar, the symbol is "$" if the specified
201     * locale is the US, while for other locales it may be "US$". If no
202     * symbol can be determined, the ISO 4217 currency code is returned.
203     *
204     * @param locale the locale for which a display name for this currency is
205     * needed
206     * @return the symbol of this currency for the specified locale
207     * @exception NullPointerException if <code>locale</code> is null
208     */
209    public String getSymbol(Locale locale) {
210        if (locale == null) {
211            throw new NullPointerException("locale == null");
212        }
213        return icuCurrency.getSymbol(locale);
214    }
215
216    /**
217     * Gets the default number of fraction digits used with this currency.
218     * For example, the default number of fraction digits for the Euro is 2,
219     * while for the Japanese Yen it's 0.
220     * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
221     * -1 is returned.
222     *
223     * @return the default number of fraction digits used with this currency
224     */
225    public int getDefaultFractionDigits() {
226        if (icuCurrency.getCurrencyCode().equals("XXX")) {
227            return -1;
228        }
229        return icuCurrency.getDefaultFractionDigits();
230    }
231
232    /**
233     * Returns the ISO 4217 numeric code of this currency.
234     *
235     * @return the ISO 4217 numeric code of this currency
236     * @since 1.7
237     */
238    public int getNumericCode() {
239        return icuCurrency.getNumericCode();
240    }
241
242    /**
243     * Gets the name that is suitable for displaying this currency for
244     * the default locale.  If there is no suitable display name found
245     * for the default locale, the ISO 4217 currency code is returned.
246     *
247     * @return the display name of this currency for the default locale
248     * @since 1.7
249     */
250    public String getDisplayName() {
251        return icuCurrency.getDisplayName();
252    }
253
254    /**
255     * Gets the name that is suitable for displaying this currency for
256     * the specified locale.  If there is no suitable display name found
257     * for the specified locale, the ISO 4217 currency code is returned.
258     *
259     * @param locale the locale for which a display name for this currency is
260     * needed
261     * @return the display name of this currency for the specified locale
262     * @exception NullPointerException if <code>locale</code> is null
263     * @since 1.7
264     */
265    public String getDisplayName(Locale locale) {
266        return icuCurrency.getDisplayName(locale);
267    }
268
269    /**
270     * Returns the ISO 4217 currency code of this currency.
271     *
272     * @return the ISO 4217 currency code of this currency
273     */
274    public String toString() {
275        return icuCurrency.toString();
276    }
277
278    /**
279     * Resolves instances being deserialized to a single instance per currency.
280     */
281    private Object readResolve() {
282        return getInstance(currencyCode);
283    }
284}
285