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