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