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