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