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