1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/**
4 *******************************************************************************
5 * Copyright (C) 2001-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 */
9package com.ibm.icu.util;
10
11import java.io.ObjectStreamException;
12import java.lang.ref.SoftReference;
13import java.text.ParsePosition;
14import java.util.ArrayList;
15import java.util.Collections;
16import java.util.Date;
17import java.util.HashMap;
18import java.util.HashSet;
19import java.util.Iterator;
20import java.util.List;
21import java.util.Locale;
22import java.util.Map;
23import java.util.MissingResourceException;
24import java.util.Set;
25
26import com.ibm.icu.impl.CacheBase;
27import com.ibm.icu.impl.CurrencyData.CurrencyDisplayInfo;
28import com.ibm.icu.impl.ICUCache;
29import com.ibm.icu.impl.ICUData;
30import com.ibm.icu.impl.ICUDebug;
31import com.ibm.icu.impl.ICUResourceBundle;
32import com.ibm.icu.impl.SimpleCache;
33import com.ibm.icu.impl.SoftCache;
34import com.ibm.icu.impl.TextTrieMap;
35import com.ibm.icu.text.CurrencyDisplayNames;
36import com.ibm.icu.text.CurrencyMetaInfo;
37import com.ibm.icu.text.CurrencyMetaInfo.CurrencyDigits;
38import com.ibm.icu.text.CurrencyMetaInfo.CurrencyFilter;
39import com.ibm.icu.util.ULocale.Category;
40
41/**
42 * A class encapsulating a currency, as defined by ISO 4217.  A
43 * <tt>Currency</tt> object can be created given a <tt>Locale</tt> or
44 * given an ISO 4217 code.  Once created, the <tt>Currency</tt> object
45 * can return various data necessary to its proper display:
46 *
47 * <ul><li>A display symbol, for a specific locale
48 * <li>The number of fraction digits to display
49 * <li>A rounding increment
50 * </ul>
51 *
52 * The <tt>DecimalFormat</tt> class uses these data to display
53 * currencies.
54 *
55 * <p>Note: This class deliberately resembles
56 * <tt>java.util.Currency</tt> but it has a completely independent
57 * implementation, and adds features not present in the JDK.
58 * @author Alan Liu
59 * @stable ICU 2.2
60 */
61public class Currency extends MeasureUnit {
62    private static final long serialVersionUID = -5839973855554750484L;
63    private static final boolean DEBUG = ICUDebug.enabled("currency");
64
65    // Cache to save currency name trie
66    private static ICUCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>> CURRENCY_NAME_CACHE =
67        new SimpleCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>>();
68
69    /**
70     * Selector for getName() indicating a symbolic name for a
71     * currency, such as "$" for USD.
72     * @stable ICU 2.6
73     */
74    public static final int SYMBOL_NAME = 0;
75
76    /**
77     * Selector for getName() indicating the long name for a
78     * currency, such as "US Dollar" for USD.
79     * @stable ICU 2.6
80     */
81    public static final int LONG_NAME = 1;
82
83    /**
84     * Selector for getName() indicating the plural long name for a
85     * currency, such as "US dollar" for USD in "1 US dollar",
86     * and "US dollars" for USD in "2 US dollars".
87     * @stable ICU 4.2
88     */
89    public static final int PLURAL_LONG_NAME = 2;
90
91    /**
92     * Selector for getName() indicating the narrow currency symbol.
93     * The narrow currency symbol is similar to the regular currency
94     * symbol, but it always takes the shortest form: for example,
95     * "$" instead of "US$".
96     *
97     * This method assumes that the currency data provider is the ICU4J
98     * built-in data provider. If it is not, an exception is thrown.
99     *
100     * @internal
101     * @deprecated ICU 60: This API is ICU internal only.
102     */
103    @Deprecated
104    public static final int NARROW_SYMBOL_NAME = 3;
105
106    private static final EquivalenceRelation<String> EQUIVALENT_CURRENCY_SYMBOLS =
107            new EquivalenceRelation<String>()
108            .add("\u00a5", "\uffe5")
109            .add("$", "\ufe69", "\uff04")
110            .add("\u20a8", "\u20b9")
111            .add("\u00a3", "\u20a4");
112
113    /**
114     * Currency Usage used for Decimal Format
115     * @stable ICU 54
116     */
117    public enum CurrencyUsage{
118        /**
119         * a setting to specify currency usage which determines currency digit and rounding
120         * for standard usage, for example: "50.00 NT$"
121         * @stable ICU 54
122         */
123        STANDARD,
124
125        /**
126         * a setting to specify currency usage which determines currency digit and rounding
127         * for cash usage, for example: "50 NT$"
128         * @stable ICU 54
129         */
130        CASH
131    }
132
133    // begin registry stuff
134
135    // shim for service code
136    /* package */ static abstract class ServiceShim {
137        abstract ULocale[] getAvailableULocales();
138        abstract Locale[] getAvailableLocales();
139        abstract Currency createInstance(ULocale l);
140        abstract Object registerInstance(Currency c, ULocale l);
141        abstract boolean unregister(Object f);
142    }
143
144    private static ServiceShim shim;
145    private static ServiceShim getShim() {
146        // Note: this instantiation is safe on loose-memory-model configurations
147        // despite lack of synchronization, since the shim instance has no state--
148        // it's all in the class init.  The worst problem is we might instantiate
149        // two shim instances, but they'll share the same state so that's ok.
150        if (shim == null) {
151            try {
152                Class<?> cls = Class.forName("com.ibm.icu.util.CurrencyServiceShim");
153                shim = (ServiceShim)cls.newInstance();
154            }
155            catch (Exception e) {
156                if(DEBUG){
157                    e.printStackTrace();
158                }
159                throw new RuntimeException(e.getMessage());
160            }
161        }
162        return shim;
163    }
164
165    /**
166     * Returns a currency object for the default currency in the given
167     * locale.
168     * @param locale the locale
169     * @return the currency object for this locale
170     * @stable ICU 2.2
171     */
172    public static Currency getInstance(Locale locale) {
173        return getInstance(ULocale.forLocale(locale));
174    }
175
176    /**
177     * Returns a currency object for the default currency in the given
178     * locale.
179     * @stable ICU 3.2
180     */
181    public static Currency getInstance(ULocale locale) {
182        String currency = locale.getKeywordValue("currency");
183        if (currency != null) {
184            return getInstance(currency);
185        }
186
187        if (shim == null) {
188            return createCurrency(locale);
189        }
190
191        return shim.createInstance(locale);
192    }
193
194    /**
195     * Returns an array of Strings which contain the currency
196     * identifiers that are valid for the given locale on the
197     * given date.  If there are no such identifiers, returns null.
198     * Returned identifiers are in preference order.
199     * @param loc the locale for which to retrieve currency codes.
200     * @param d the date for which to retrieve currency codes for the given locale.
201     * @return The array of ISO currency codes.
202     * @stable ICU 4.0
203     */
204    public static String[] getAvailableCurrencyCodes(ULocale loc, Date d) {
205        String region = ULocale.getRegionForSupplementalData(loc, false);
206        CurrencyFilter filter = CurrencyFilter.onDate(d).withRegion(region);
207        List<String> list = getTenderCurrencies(filter);
208        // Note: Prior to 4.4 the spec didn't say that we return null if there are no results, but
209        // the test assumed it did.  Kept the behavior and amended the spec.
210        if (list.isEmpty()) {
211            return null;
212        }
213        return list.toArray(new String[list.size()]);
214    }
215
216    /**
217     * Returns an array of Strings which contain the currency
218     * identifiers that are valid for the given {@link java.util.Locale} on the
219     * given date.  If there are no such identifiers, returns null.
220     * Returned identifiers are in preference order.
221     * @param loc the {@link java.util.Locale} for which to retrieve currency codes.
222     * @param d the date for which to retrieve currency codes for the given locale.
223     * @return The array of ISO currency codes.
224     * @stable ICU 54
225     */
226    public static String[] getAvailableCurrencyCodes(Locale loc, Date d) {
227        return getAvailableCurrencyCodes(ULocale.forLocale(loc), d);
228    }
229
230    /**
231     * Returns the set of available currencies. The returned set of currencies contains all of the
232     * available currencies, including obsolete ones. The result set can be modified without
233     * affecting the available currencies in the runtime.
234     *
235     * @return The set of available currencies. The returned set could be empty if there is no
236     * currency data available.
237     *
238     * @stable ICU 49
239     */
240    public static Set<Currency> getAvailableCurrencies() {
241        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
242        List<String> list = info.currencies(CurrencyFilter.all());
243        HashSet<Currency> resultSet = new HashSet<Currency>(list.size());
244        for (String code : list) {
245            resultSet.add(getInstance(code));
246        }
247        return resultSet;
248    }
249
250    private static final String EUR_STR = "EUR";
251    private static final CacheBase<String, Currency, Void> regionCurrencyCache =
252            new SoftCache<String, Currency, Void>() {
253        @Override
254        protected Currency createInstance(String key, Void unused) {
255            return loadCurrency(key);
256        }
257    };
258
259    /**
260     * Instantiate a currency from resource data.
261     */
262    /* package */ static Currency createCurrency(ULocale loc) {
263        String variant = loc.getVariant();
264        if ("EURO".equals(variant)) {
265            return getInstance(EUR_STR);
266        }
267
268        // Cache the currency by region, and whether variant=PREEURO.
269        // Minimizes the size of the cache compared with caching by ULocale.
270        String key = ULocale.getRegionForSupplementalData(loc, false);
271        if ("PREEURO".equals(variant)) {
272            key = key + '-';
273        }
274        return regionCurrencyCache.getInstance(key, null);
275    }
276
277    private static Currency loadCurrency(String key) {
278        String region;
279        boolean isPreEuro;
280        if (key.endsWith("-")) {
281            region = key.substring(0, key.length() - 1);
282            isPreEuro = true;
283        } else {
284            region = key;
285            isPreEuro = false;
286        }
287        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
288        List<String> list = info.currencies(CurrencyFilter.onRegion(region));
289        if (!list.isEmpty()) {
290            String code = list.get(0);
291            if (isPreEuro && EUR_STR.equals(code)) {
292                if (list.size() < 2) {
293                    return null;
294                }
295                code = list.get(1);
296            }
297            return getInstance(code);
298        }
299        return null;
300    }
301
302    /**
303     * Returns a currency object given an ISO 4217 3-letter code.
304     * @param theISOCode the iso code
305     * @return the currency for this iso code
306     * @throws NullPointerException if <code>theISOCode</code> is null.
307     * @throws IllegalArgumentException if <code>theISOCode</code> is not a
308     *         3-letter alpha code.
309     * @stable ICU 2.2
310     */
311    public static Currency getInstance(String theISOCode) {
312        if (theISOCode == null) {
313            throw new NullPointerException("The input currency code is null.");
314        }
315        if (!isAlpha3Code(theISOCode)) {
316            throw new IllegalArgumentException(
317                    "The input currency code is not 3-letter alphabetic code.");
318        }
319        return (Currency) MeasureUnit.internalGetInstance("currency", theISOCode.toUpperCase(Locale.ENGLISH));
320    }
321
322
323    private static boolean isAlpha3Code(String code) {
324        if (code.length() != 3) {
325            return false;
326        } else {
327            for (int i = 0; i < 3; i++) {
328                char ch = code.charAt(i);
329                if (ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') {
330                    return false;
331                }
332            }
333        }
334        return true;
335    }
336
337    /**
338     * Returns a Currency object based on the currency represented by the given java.util.Currency.
339     *
340     * @param currency The Java currency object to convert.
341     * @return An equivalent ICU currency object.
342     * @draft ICU 60
343     */
344    public static Currency fromJavaCurrency(java.util.Currency currency) {
345        return getInstance(currency.getCurrencyCode());
346    }
347
348    /**
349     * Returns a java.util.Currency object based on the currency represented by this Currency.
350     *
351     * @return An equivalent Java currency object.
352     * @draft ICU 60
353     */
354    public java.util.Currency toJavaCurrency() {
355        return java.util.Currency.getInstance(getCurrencyCode());
356    }
357
358    /**
359     * Registers a new currency for the provided locale.  The returned object
360     * is a key that can be used to unregister this currency object.
361     *
362     * <p>Because ICU may choose to cache Currency objects internally, this must
363     * be called at application startup, prior to any calls to
364     * Currency.getInstance to avoid undefined behavior.
365     *
366     * @param currency the currency to register
367     * @param locale the ulocale under which to register the currency
368     * @return a registry key that can be used to unregister this currency
369     * @see #unregister
370     * @stable ICU 3.2
371     */
372    public static Object registerInstance(Currency currency, ULocale locale) {
373        return getShim().registerInstance(currency, locale);
374    }
375
376    /**
377     * Unregister the currency associated with this key (obtained from
378     * registerInstance).
379     * @param registryKey the registry key returned from registerInstance
380     * @see #registerInstance
381     * @stable ICU 2.6
382     */
383    public static boolean unregister(Object registryKey) {
384        if (registryKey == null) {
385            throw new IllegalArgumentException("registryKey must not be null");
386        }
387        if (shim == null) {
388            return false;
389        }
390        return shim.unregister(registryKey);
391    }
392
393    /**
394     * Return an array of the locales for which a currency
395     * is defined.
396     * @return an array of the available locales
397     * @stable ICU 2.2
398     */
399    public static Locale[] getAvailableLocales() {
400        if (shim == null) {
401            return ICUResourceBundle.getAvailableLocales();
402        } else {
403            return shim.getAvailableLocales();
404        }
405    }
406
407    /**
408     * Return an array of the ulocales for which a currency
409     * is defined.
410     * @return an array of the available ulocales
411     * @stable ICU 3.2
412     */
413    public static ULocale[] getAvailableULocales() {
414        if (shim == null) {
415            return ICUResourceBundle.getAvailableULocales();
416        } else {
417            return shim.getAvailableULocales();
418        }
419    }
420
421    // end registry stuff
422
423    /**
424     * Given a key and a locale, returns an array of values for the key for which data
425     * exists.  If commonlyUsed is true, these are the values that typically are used
426     * with this locale, otherwise these are all values for which data exists.
427     * This is a common service API.
428     * <p>
429     * The only supported key is "currency", other values return an empty array.
430     * <p>
431     * Currency information is based on the region of the locale.  If the locale does not
432     * indicate a region, {@link ULocale#addLikelySubtags(ULocale)} is used to infer a region,
433     * except for the 'und' locale.
434     * <p>
435     * If commonlyUsed is true, only the currencies known to be in use as of the current date
436     * are returned.  When there are more than one, these are returned in preference order
437     * (typically, this occurs when a country is transitioning to a new currency, and the
438     * newer currency is preferred), see
439     * <a href="http://unicode.org/reports/tr35/#Supplemental_Currency_Data">Unicode TR#35 Sec. C1</a>.
440     * If commonlyUsed is false, all currencies ever used in any locale are returned, in no
441     * particular order.
442     *
443     * @param key           key whose values to look up.  the only recognized key is "currency"
444     * @param locale        the locale
445     * @param commonlyUsed  if true, return only values that are currently used in the locale.
446     *                      Otherwise returns all values.
447     * @return an array of values for the given key and the locale.  If there is no data, the
448     *   array will be empty.
449     * @stable ICU 4.2
450     */
451    public static final String[] getKeywordValuesForLocale(String key, ULocale locale,
452            boolean commonlyUsed) {
453
454        // The only keyword we recognize is 'currency'
455        if (!"currency".equals(key)) {
456            return EMPTY_STRING_ARRAY;
457        }
458
459        if (!commonlyUsed) {
460            // Behavior change from 4.3.3, no longer sort the currencies
461            return getAllTenderCurrencies().toArray(new String[0]);
462        }
463
464        // Don't resolve region if the requested locale is 'und', it will resolve to US
465        // which we don't want.
466        if (UND.equals(locale)) {
467            return EMPTY_STRING_ARRAY;
468        }
469        String prefRegion = ULocale.getRegionForSupplementalData(locale, true);
470
471        CurrencyFilter filter = CurrencyFilter.now().withRegion(prefRegion);
472
473        // currencies are in region's preferred order when we're filtering on region, which
474        // matches our spec
475        List<String> result = getTenderCurrencies(filter);
476
477        // No fallback anymore (change from 4.3.3)
478        if (result.size() == 0) {
479            return EMPTY_STRING_ARRAY;
480        }
481
482        return result.toArray(new String[result.size()]);
483    }
484
485    private static final ULocale UND = new ULocale("und");
486    private static final String[] EMPTY_STRING_ARRAY = new String[0];
487
488    /**
489     * Returns the ISO 4217 3-letter code for this currency object.
490     * @stable ICU 2.2
491     */
492    public String getCurrencyCode() {
493        return subType;
494    }
495
496    /**
497     * Returns the ISO 4217 numeric code for this currency object.
498     * <p>Note: If the ISO 4217 numeric code is not assigned for the currency or
499     * the currency is unknown, this method returns 0.</p>
500     * @return The ISO 4217 numeric code of this currency.
501     * @stable ICU 49
502     */
503    public int getNumericCode() {
504        int result = 0;
505        try {
506            UResourceBundle bundle = UResourceBundle.getBundleInstance(
507                    ICUData.ICU_BASE_NAME,
508                    "currencyNumericCodes",
509                    ICUResourceBundle.ICU_DATA_CLASS_LOADER);
510            UResourceBundle codeMap = bundle.get("codeMap");
511            UResourceBundle numCode = codeMap.get(subType);
512            result = numCode.getInt();
513        } catch (MissingResourceException e) {
514            // fall through
515        }
516        return result;
517    }
518
519    /**
520     * Convenience and compatibility override of getName that
521     * requests the symbol name for the default <code>DISPLAY</code> locale.
522     * @see #getName
523     * @see Category#DISPLAY
524     * @stable ICU 3.4
525     */
526    public String getSymbol() {
527        return getSymbol(ULocale.getDefault(Category.DISPLAY));
528    }
529
530    /**
531     * Convenience and compatibility override of getName that
532     * requests the symbol name.
533     * @param loc the Locale for the symbol
534     * @see #getName
535     * @stable ICU 3.4
536     */
537    public String getSymbol(Locale loc) {
538        return getSymbol(ULocale.forLocale(loc));
539    }
540
541    /**
542     * Convenience and compatibility override of getName that
543     * requests the symbol name.
544     * @param uloc the ULocale for the symbol
545     * @see #getName
546     * @stable ICU 3.4
547     */
548    public String getSymbol(ULocale uloc) {
549        return getName(uloc, SYMBOL_NAME, new boolean[1]);
550    }
551
552    /**
553     * Returns the display name for the given currency in the
554     * given locale.
555     * This is a convenient method for
556     * getName(ULocale, int, boolean[]);
557     * @stable ICU 3.2
558     */
559    public String getName(Locale locale,
560                          int nameStyle,
561                          boolean[] isChoiceFormat) {
562        return getName(ULocale.forLocale(locale), nameStyle, isChoiceFormat);
563    }
564
565    /**
566     * Returns the display name for the given currency in the
567     * given locale.  For example, the display name for the USD
568     * currency object in the en_US locale is "$".
569     * @param locale locale in which to display currency
570     * @param nameStyle selector for which kind of name to return.
571     *                  The nameStyle should be either SYMBOL_NAME or
572     *                  LONG_NAME. Otherwise, throw IllegalArgumentException.
573     * @param isChoiceFormat fill-in; isChoiceFormat[0] is set to true
574     * if the returned value is a ChoiceFormat pattern; otherwise it
575     * is set to false
576     * @return display string for this currency.  If the resource data
577     * contains no entry for this currency, then the ISO 4217 code is
578     * returned.  If isChoiceFormat[0] is true, then the result is a
579     * ChoiceFormat pattern.  Otherwise it is a static string. <b>Note:</b>
580     * as of ICU 4.4, choice formats are not used, and the value returned
581     * in isChoiceFormat is always false.
582     * <p>
583     * @throws  IllegalArgumentException  if the nameStyle is not SYMBOL_NAME
584     *                                    or LONG_NAME.
585     * @see #getName(ULocale, int, String, boolean[])
586     * @stable ICU 3.2
587     */
588    public String getName(ULocale locale, int nameStyle, boolean[] isChoiceFormat) {
589        // We no longer support choice format data in names.  Data should not contain
590        // choice patterns.
591        if (isChoiceFormat != null) {
592            isChoiceFormat[0] = false;
593        }
594
595        CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
596        switch (nameStyle) {
597        case SYMBOL_NAME:
598            return names.getSymbol(subType);
599        case NARROW_SYMBOL_NAME:
600            // CurrencyDisplayNames is the public interface.
601            // CurrencyDisplayInfo is ICU's standard implementation.
602            if (!(names instanceof CurrencyDisplayInfo)) {
603                throw new UnsupportedOperationException(
604                        "Cannot get narrow symbol from custom currency display name provider");
605            }
606            return ((CurrencyDisplayInfo) names).getNarrowSymbol(subType);
607        case LONG_NAME:
608            return names.getName(subType);
609        default:
610            throw new IllegalArgumentException("bad name style: " + nameStyle);
611        }
612    }
613
614    /**
615     * Returns the display name for the given currency in the given locale.
616     * This is a convenience overload of getName(ULocale, int, String, boolean[]);
617     * @stable ICU 4.2
618     */
619    public String getName(Locale locale, int nameStyle, String pluralCount,
620            boolean[] isChoiceFormat) {
621        return getName(ULocale.forLocale(locale), nameStyle, pluralCount, isChoiceFormat);
622    }
623
624    /**
625     * Returns the display name for the given currency in the
626     * given locale.  For example, the SYMBOL_NAME for the USD
627     * currency object in the en_US locale is "$".
628     * The PLURAL_LONG_NAME for the USD currency object when the currency
629     * amount is plural is "US dollars", such as in "3.00 US dollars";
630     * while the PLURAL_LONG_NAME for the USD currency object when the currency
631     * amount is singular is "US dollar", such as in "1.00 US dollar".
632     * @param locale locale in which to display currency
633     * @param nameStyle selector for which kind of name to return
634     * @param pluralCount plural count string for this locale
635     * @param isChoiceFormat fill-in; isChoiceFormat[0] is set to true
636     * if the returned value is a ChoiceFormat pattern; otherwise it
637     * is set to false
638     * @return display string for this currency.  If the resource data
639     * contains no entry for this currency, then the ISO 4217 code is
640     * returned.  If isChoiceFormat[0] is true, then the result is a
641     * ChoiceFormat pattern.  Otherwise it is a static string. <b>Note:</b>
642     * as of ICU 4.4, choice formats are not used, and the value returned
643     * in isChoiceFormat is always false.
644     * @throws  IllegalArgumentException  if the nameStyle is not SYMBOL_NAME,
645     *                                    LONG_NAME, or PLURAL_LONG_NAME.
646     * @stable ICU 4.2
647     */
648    public String getName(ULocale locale, int nameStyle, String pluralCount,
649            boolean[] isChoiceFormat) {
650        if (nameStyle != PLURAL_LONG_NAME) {
651            return getName(locale, nameStyle, isChoiceFormat);
652        }
653
654        // We no longer support choice format
655        if (isChoiceFormat != null) {
656            isChoiceFormat[0] = false;
657        }
658
659        CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
660        return names.getPluralName(subType, pluralCount);
661    }
662
663    /**
664     * Returns the display name for this currency in the default locale.
665     * If the resource data for the default locale contains no entry for this currency,
666     * then the ISO 4217 code is returned.
667     * <p>
668     * Note: This method is a convenience equivalent for
669     * {@link java.util.Currency#getDisplayName()} and is equivalent to
670     * <code>getName(Locale.getDefault(), LONG_NAME, null)</code>.
671     *
672     * @return The display name of this currency
673     * @see #getDisplayName(Locale)
674     * @see #getName(Locale, int, boolean[])
675     * @stable ICU 49
676     */
677    @SuppressWarnings("javadoc")    // java.util.Currency#getDisplayName() is introduced in Java 7
678    public String getDisplayName() {
679        return getName(Locale.getDefault(), LONG_NAME, null);
680    }
681
682    /**
683     * Returns the display name for this currency in the given locale.
684     * If the resource data for the given locale contains no entry for this currency,
685     * then the ISO 4217 code is returned.
686     * <p>
687     * Note: This method is a convenience equivalent for
688     * {@link java.util.Currency#getDisplayName(java.util.Locale)} and is equivalent
689     * to <code>getName(locale, LONG_NAME, null)</code>.
690     *
691     * @param locale locale in which to display currency
692     * @return The display name of this currency for the specified locale
693     * @see #getDisplayName(Locale)
694     * @see #getName(Locale, int, boolean[])
695     * @stable ICU 49
696     */
697    @SuppressWarnings("javadoc")    // java.util.Currency#getDisplayName() is introduced in Java 7
698    public String getDisplayName(Locale locale) {
699        return getName(locale, LONG_NAME, null);
700    }
701
702    /**
703     * Attempt to parse the given string as a currency, either as a
704     * display name in the given locale, or as a 3-letter ISO 4217
705     * code.  If multiple display names match, then the longest one is
706     * selected.  If both a display name and a 3-letter ISO code
707     * match, then the display name is preferred, unless it's length
708     * is less than 3.
709     *
710     * @param locale the locale of the display names to match
711     * @param text the text to parse
712     * @param type parse against currency type: LONG_NAME only or not
713     * @param pos input-output position; on input, the position within
714     * text to match; must have 0 &lt;= pos.getIndex() &lt; text.length();
715     * on output, the position after the last matched character. If
716     * the parse fails, the position in unchanged upon output.
717     * @return the ISO 4217 code, as a string, of the best match, or
718     * null if there is no match
719     *
720     * @internal
721     * @deprecated This API is ICU internal only.
722     */
723    @Deprecated
724    public static String parse(ULocale locale, String text, int type, ParsePosition pos) {
725        List<TextTrieMap<CurrencyStringInfo>> currencyTrieVec = getCurrencyTrieVec(locale);
726        int maxLength = 0;
727        String isoResult = null;
728
729          // look for the names
730        TextTrieMap<CurrencyStringInfo> currencyNameTrie = currencyTrieVec.get(1);
731        CurrencyNameResultHandler handler = new CurrencyNameResultHandler();
732        currencyNameTrie.find(text, pos.getIndex(), handler);
733        isoResult = handler.getBestCurrencyISOCode();
734        maxLength = handler.getBestMatchLength();
735
736        if (type != Currency.LONG_NAME) {  // not long name only
737            TextTrieMap<CurrencyStringInfo> currencySymbolTrie = currencyTrieVec.get(0);
738            handler = new CurrencyNameResultHandler();
739            currencySymbolTrie.find(text, pos.getIndex(), handler);
740            if (handler.getBestMatchLength() > maxLength) {
741                isoResult = handler.getBestCurrencyISOCode();
742                maxLength = handler.getBestMatchLength();
743            }
744        }
745        int start = pos.getIndex();
746        pos.setIndex(start + maxLength);
747        return isoResult;
748    }
749
750    /**
751     * @internal
752     * @deprecated This API is ICU internal only.
753     */
754    @Deprecated
755    public static TextTrieMap<CurrencyStringInfo>.ParseState openParseState(
756        ULocale locale, int startingCp, int type) {
757        List<TextTrieMap<CurrencyStringInfo>> currencyTrieVec = getCurrencyTrieVec(locale);
758        if (type == Currency.LONG_NAME) {
759            return currencyTrieVec.get(0).openParseState(startingCp);
760        } else {
761            return currencyTrieVec.get(1).openParseState(startingCp);
762        }
763    }
764
765    private static List<TextTrieMap<CurrencyStringInfo>> getCurrencyTrieVec(ULocale locale) {
766        List<TextTrieMap<CurrencyStringInfo>> currencyTrieVec = CURRENCY_NAME_CACHE.get(locale);
767        if (currencyTrieVec == null) {
768            TextTrieMap<CurrencyStringInfo> currencyNameTrie =
769                new TextTrieMap<CurrencyStringInfo>(true);
770            TextTrieMap<CurrencyStringInfo> currencySymbolTrie =
771                new TextTrieMap<CurrencyStringInfo>(false);
772            currencyTrieVec = new ArrayList<TextTrieMap<CurrencyStringInfo>>();
773            currencyTrieVec.add(currencySymbolTrie);
774            currencyTrieVec.add(currencyNameTrie);
775            setupCurrencyTrieVec(locale, currencyTrieVec);
776            CURRENCY_NAME_CACHE.put(locale, currencyTrieVec);
777        }
778        return currencyTrieVec;
779    }
780
781    private static void setupCurrencyTrieVec(ULocale locale,
782            List<TextTrieMap<CurrencyStringInfo>> trieVec) {
783
784        TextTrieMap<CurrencyStringInfo> symTrie = trieVec.get(0);
785        TextTrieMap<CurrencyStringInfo> trie = trieVec.get(1);
786
787        CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
788        for (Map.Entry<String, String> e : names.symbolMap().entrySet()) {
789            String symbol = e.getKey();
790            String isoCode = e.getValue();
791            // Register under not just symbol, but under every equivalent symbol as well
792            // e.g short width yen and long width yen.
793            for (String equivalentSymbol : EQUIVALENT_CURRENCY_SYMBOLS.get(symbol)) {
794                symTrie.put(equivalentSymbol, new CurrencyStringInfo(isoCode, symbol));
795            }
796        }
797        for (Map.Entry<String, String> e : names.nameMap().entrySet()) {
798            String name = e.getKey();
799            String isoCode = e.getValue();
800            trie.put(name, new CurrencyStringInfo(isoCode, name));
801        }
802    }
803
804    /**
805     * @internal
806     * @deprecated This API is ICU internal only.
807     */
808    @Deprecated
809    public static final class CurrencyStringInfo {
810        private String isoCode;
811        private String currencyString;
812
813        /**
814         * @internal
815         * @deprecated This API is ICU internal only.
816         */
817        @Deprecated
818        public CurrencyStringInfo(String isoCode, String currencyString) {
819            this.isoCode = isoCode;
820            this.currencyString = currencyString;
821        }
822
823        /**
824         * @internal
825         * @deprecated This API is ICU internal only.
826         */
827        @Deprecated
828        public String getISOCode() {
829            return isoCode;
830        }
831
832        /**
833         * @internal
834         * @deprecated This API is ICU internal only.
835         */
836        @Deprecated
837        @SuppressWarnings("unused")
838        public String getCurrencyString() {
839            return currencyString;
840        }
841    }
842
843    private static class CurrencyNameResultHandler
844            implements TextTrieMap.ResultHandler<CurrencyStringInfo> {
845        // The length of longest matching key
846        private int bestMatchLength;
847        // The currency ISO code of longest matching key
848        private String bestCurrencyISOCode;
849
850        // As the trie is traversed, handlePrefixMatch is called at each node. matchLength is the
851        // length length of the key at the current node; values is the list of all the values mapped to
852        // that key. matchLength increases with each call as trie is traversed.
853        @Override
854        public boolean handlePrefixMatch(int matchLength, Iterator<CurrencyStringInfo> values) {
855            if (values.hasNext()) {
856                // Since the best match criteria is only based on length of key in trie and since all the
857                // values are mapped to the same key, we only need to examine the first value.
858                bestCurrencyISOCode = values.next().getISOCode();
859                bestMatchLength = matchLength;
860            }
861            return true;
862        }
863
864        public String getBestCurrencyISOCode() {
865            return bestCurrencyISOCode;
866        }
867
868        public int getBestMatchLength() {
869            return bestMatchLength;
870        }
871    }
872
873    /**
874     * Returns the number of the number of fraction digits that should
875     * be displayed for this currency.
876     * This is equivalent to getDefaultFractionDigits(CurrencyUsage.STANDARD);
877     * @return a non-negative number of fraction digits to be
878     * displayed
879     * @stable ICU 2.2
880     */
881    public int getDefaultFractionDigits() {
882        return getDefaultFractionDigits(CurrencyUsage.STANDARD);
883    }
884
885    /**
886     * Returns the number of the number of fraction digits that should
887     * be displayed for this currency with Usage.
888     * @param Usage the usage of currency(Standard or Cash)
889     * @return a non-negative number of fraction digits to be
890     * displayed
891     * @stable ICU 54
892     */
893    public int getDefaultFractionDigits(CurrencyUsage Usage) {
894        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
895        CurrencyDigits digits = info.currencyDigits(subType, Usage);
896        return digits.fractionDigits;
897    }
898
899    /**
900     * Returns the rounding increment for this currency, or 0.0 if no
901     * rounding is done by this currency.
902     * This is equivalent to getRoundingIncrement(CurrencyUsage.STANDARD);
903     * @return the non-negative rounding increment, or 0.0 if none
904     * @stable ICU 2.2
905     */
906    public double getRoundingIncrement() {
907        return getRoundingIncrement(CurrencyUsage.STANDARD);
908    }
909
910    /**
911     * Returns the rounding increment for this currency, or 0.0 if no
912     * rounding is done by this currency with the Usage.
913     * @param Usage the usage of currency(Standard or Cash)
914     * @return the non-negative rounding increment, or 0.0 if none
915     * @stable ICU 54
916     */
917    public double getRoundingIncrement(CurrencyUsage Usage) {
918        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
919        CurrencyDigits digits = info.currencyDigits(subType, Usage);
920
921        int data1 = digits.roundingIncrement;
922
923        // If there is no rounding return 0.0 to indicate no rounding.
924        // This is the high-runner case, by far.
925        if (data1 == 0) {
926            return 0.0;
927        }
928
929        int data0 = digits.fractionDigits;
930
931        // If the meta data is invalid, return 0.0 to indicate no rounding.
932        if (data0 < 0 || data0 >= POW10.length) {
933            return 0.0;
934        }
935
936        // Return data[1] / 10^(data[0]). The only actual rounding data,
937        // as of this writing, is CHF { 2, 25 }.
938        return (double) data1 / POW10[data0];
939    }
940
941    /**
942     * Returns the ISO 4217 code for this currency.
943     * @stable ICU 2.2
944     */
945    @Override
946    public String toString() {
947        return subType;
948    }
949
950    /**
951     * Constructs a currency object for the given ISO 4217 3-letter
952     * code.  This constructor assumes that the code is valid.
953     *
954     * @param theISOCode The iso code used to construct the currency.
955     * @stable ICU 3.4
956     */
957    protected Currency(String theISOCode) {
958        super("currency", theISOCode);
959
960        // isoCode is kept for readResolve() and Currency class no longer
961        // use it. So this statement actually does not have any effect.
962        isoCode = theISOCode;
963    }
964
965    // POW10[i] = 10^i
966    private static final int[] POW10 = {
967        1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
968    };
969
970
971    private static SoftReference<List<String>> ALL_TENDER_CODES;
972    private static SoftReference<Set<String>> ALL_CODES_AS_SET;
973    /*
974     * Returns an unmodifiable String list including all known tender currency codes.
975     */
976    private static synchronized List<String> getAllTenderCurrencies() {
977        List<String> all = (ALL_TENDER_CODES == null) ? null : ALL_TENDER_CODES.get();
978        if (all == null) {
979            // Filter out non-tender currencies which have "from" date set to 9999-12-31
980            // CurrencyFilter has "to" value set to 9998-12-31 in order to exclude them
981            //CurrencyFilter filter = CurrencyFilter.onDateRange(null, new Date(253373299200000L));
982            CurrencyFilter filter = CurrencyFilter.all();
983            all = Collections.unmodifiableList(getTenderCurrencies(filter));
984            ALL_TENDER_CODES = new SoftReference<List<String>>(all);
985        }
986        return all;
987    }
988
989    private static synchronized Set<String> getAllCurrenciesAsSet() {
990        Set<String> all = (ALL_CODES_AS_SET == null) ? null : ALL_CODES_AS_SET.get();
991        if (all == null) {
992            CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
993            all = Collections.unmodifiableSet(
994                    new HashSet<String>(info.currencies(CurrencyFilter.all())));
995            ALL_CODES_AS_SET = new SoftReference<Set<String>>(all);
996        }
997        return all;
998    }
999
1000    /**
1001     * Queries if the given ISO 4217 3-letter code is available on the specified date range.
1002     * <p>
1003     * Note: For checking availability of a currency on a specific date, specify the date on both <code>from</code> and
1004     * <code>to</code>. When both <code>from</code> and <code>to</code> are null, this method checks if the specified
1005     * currency is available all time.
1006     *
1007     * @param code
1008     *            The ISO 4217 3-letter code.
1009     * @param from
1010     *            The lower bound of the date range, inclusive. When <code>from</code> is null, check the availability
1011     *            of the currency any date before <code>to</code>
1012     * @param to
1013     *            The upper bound of the date range, inclusive. When <code>to</code> is null, check the availability of
1014     *            the currency any date after <code>from</code>
1015     * @return true if the given ISO 4217 3-letter code is supported on the specified date range.
1016     * @throws IllegalArgumentException when <code>to</code> is before <code>from</code>.
1017     *
1018     * @stable ICU 4.6
1019     */
1020    public static boolean isAvailable(String code, Date from, Date to) {
1021        if (!isAlpha3Code(code)) {
1022            return false;
1023        }
1024
1025        if (from != null && to != null && from.after(to)) {
1026            throw new IllegalArgumentException("To is before from");
1027        }
1028
1029        code = code.toUpperCase(Locale.ENGLISH);
1030        boolean isKnown = getAllCurrenciesAsSet().contains(code);
1031        if (isKnown == false) {
1032            return false;
1033        } else if (from == null && to == null) {
1034            return true;
1035        }
1036
1037        // If caller passed a date range, we cannot rely solely on the cache
1038        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
1039        List<String> allActive = info.currencies(
1040                CurrencyFilter.onDateRange(from, to).withCurrency(code));
1041        return allActive.contains(code);
1042    }
1043
1044    /**
1045     * Returns the list of remaining tender currencies after a filter is applied.
1046     * @param filter the filter to apply to the tender currencies
1047     * @return a list of tender currencies
1048     */
1049    private static List<String> getTenderCurrencies(CurrencyFilter filter) {
1050        CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
1051        return info.currencies(filter.withTender());
1052    }
1053
1054    private static final class EquivalenceRelation<T> {
1055
1056        private Map<T, Set<T>> data = new HashMap<T, Set<T>>();
1057
1058        @SuppressWarnings("unchecked")  // See ticket #11395, this is safe.
1059        public EquivalenceRelation<T> add(T... items) {
1060            Set<T> group = new HashSet<T>();
1061            for (T item : items) {
1062                if (data.containsKey(item)) {
1063                    throw new IllegalArgumentException("All groups passed to add must be disjoint.");
1064                }
1065                group.add(item);
1066            }
1067            for (T item : items) {
1068                data.put(item, group);
1069            }
1070            return this;
1071        }
1072
1073        public Set<T> get(T item) {
1074            Set<T> result = data.get(item);
1075            if (result == null) {
1076                return Collections.singleton(item);
1077            }
1078            return Collections.unmodifiableSet(result);
1079        }
1080    }
1081
1082    private Object writeReplace() throws ObjectStreamException {
1083        return new MeasureUnitProxy(type, subType);
1084    }
1085
1086    // For backward compatibility only
1087    /**
1088     * ISO 4217 3-letter code.
1089     */
1090    private final String isoCode;
1091
1092    private Object readResolve() throws ObjectStreamException {
1093        // The old isoCode field used to determine the currency.
1094        return Currency.getInstance(isoCode);
1095    }
1096}
1097//eof
1098