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