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