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