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