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