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