1 /* 2 ******************************************************************************* 3 * Copyright (C) 2009-2014, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 package com.ibm.icu.text; 8 9 import java.lang.reflect.Field; 10 import java.util.Collections; 11 import java.util.Date; 12 import java.util.List; 13 14 import com.ibm.icu.impl.Grego; 15 import com.ibm.icu.util.Currency.CurrencyUsage; 16 17 /** 18 * Provides information about currencies that is not specific to a locale. 19 * 20 * A note about currency dates. The CLDR data provides data to the day, 21 * inclusive. The date information used by CurrencyInfo and CurrencyFilter 22 * is represented by milliseconds, which is overly precise. These times are 23 * in GMT, so queries involving dates should use GMT times, but more generally 24 * you should avoid relying on time of day in queries. 25 * 26 * This class is not intended for public subclassing. 27 * 28 * @stable ICU 4.4 29 */ 30 public class CurrencyMetaInfo { 31 private static final CurrencyMetaInfo impl; 32 private static final boolean hasData; 33 34 /** 35 * Returns the unique instance of the currency meta info. 36 * @return the meta info 37 * @stable ICU 4.4 38 */ getInstance()39 public static CurrencyMetaInfo getInstance() { 40 return impl; 41 } 42 43 /** 44 * Returns the unique instance of the currency meta info, or null if 45 * noSubstitute is true and there is no data to support this API. 46 * @param noSubstitute true if no substitute data should be used 47 * @return the meta info, or null 48 * @stable ICU 49 49 */ getInstance(boolean noSubstitute)50 public static CurrencyMetaInfo getInstance(boolean noSubstitute) { 51 return hasData ? impl : null; 52 } 53 54 /** 55 * Returns true if there is data for the currency meta info. 56 * @return true if there is actual data 57 * @internal 58 * @deprecated This API is ICU internal only. 59 */ 60 @Deprecated hasData()61 public static boolean hasData() { 62 return hasData; 63 } 64 65 /** 66 * Subclass constructor. 67 * @internal 68 * @deprecated This API is ICU internal only. 69 */ 70 @Deprecated CurrencyMetaInfo()71 protected CurrencyMetaInfo() { 72 } 73 74 /** 75 * A filter used to select which currency info is returned. 76 * @stable ICU 4.4 77 */ 78 public static final class CurrencyFilter { 79 /** 80 * The region to filter on. If null, accepts any region. 81 * @stable ICU 4.4 82 */ 83 public final String region; 84 85 /** 86 * The currency to filter on. If null, accepts any currency. 87 * @stable ICU 4.4 88 */ 89 public final String currency; 90 91 /** 92 * The from date to filter on (as milliseconds). Accepts any currency on or after this date. 93 * @stable ICU 4.4 94 */ 95 public final long from; 96 97 /** 98 * The to date to filter on (as milliseconds). Accepts any currency on or before this date. 99 * @stable ICU 4.4 100 */ 101 public final long to; 102 103 /** 104 * true if we are filtering only for currencies used as legal tender. 105 * @internal 106 * @deprecated This API is ICU internal only. 107 */ 108 @Deprecated 109 public final boolean tenderOnly; 110 CurrencyFilter(String region, String currency, long from, long to, boolean tenderOnly)111 private CurrencyFilter(String region, String currency, long from, long to, boolean tenderOnly) { 112 this.region = region; 113 this.currency = currency; 114 this.from = from; 115 this.to = to; 116 this.tenderOnly = tenderOnly; 117 118 } 119 120 private static final CurrencyFilter ALL = new CurrencyFilter( 121 null, null, Long.MIN_VALUE, Long.MAX_VALUE, false); 122 123 /** 124 * Returns a filter that accepts all currency data. 125 * @return a filter 126 * @stable ICU 4.4 127 */ all()128 public static CurrencyFilter all() { 129 return ALL; 130 } 131 132 /** 133 * Returns a filter that accepts all currencies in use as of the current date. 134 * @return a filter 135 * @see #withDate(Date) 136 * @stable ICU 4.4 137 */ now()138 public static CurrencyFilter now() { 139 return ALL.withDate(new Date()); 140 } 141 142 /** 143 * Returns a filter that accepts all currencies ever used in the given region. 144 * @param region the region code 145 * @return a filter 146 * @see #withRegion(String) 147 * @stable ICU 4.4 148 */ onRegion(String region)149 public static CurrencyFilter onRegion(String region) { 150 return ALL.withRegion(region); 151 } 152 153 /** 154 * Returns a filter that accepts the given currency. 155 * @param currency the currency code 156 * @return a filter 157 * @see #withCurrency(String) 158 * @stable ICU 4.4 159 */ onCurrency(String currency)160 public static CurrencyFilter onCurrency(String currency) { 161 return ALL.withCurrency(currency); 162 } 163 164 /** 165 * Returns a filter that accepts all currencies in use on the given date. 166 * @param date the date 167 * @return a filter 168 * @see #withDate(Date) 169 * @stable ICU 4.4 170 */ onDate(Date date)171 public static CurrencyFilter onDate(Date date) { 172 return ALL.withDate(date); 173 } 174 175 /** 176 * Returns a filter that accepts all currencies that were in use at some point between 177 * the given dates, or if dates are equal, currencies in use on that date. 178 * @param from date on or after a currency must have been in use 179 * @param to date on or before which a currency must have been in use, 180 * or if equal to from, the date on which a currency must have been in use 181 * @return a filter 182 * @see #withDateRange(Date, Date) 183 * @stable ICU 49 184 */ onDateRange(Date from, Date to)185 public static CurrencyFilter onDateRange(Date from, Date to) { 186 return ALL.withDateRange(from, to); 187 } 188 189 /** 190 * Returns a filter that accepts all currencies in use on the given date. 191 * @param date the date as milliseconds after Jan 1, 1970 192 * @stable ICU 51 193 */ onDate(long date)194 public static CurrencyFilter onDate(long date) { 195 return ALL.withDate(date); 196 } 197 198 /** 199 * Returns a filter that accepts all currencies that were in use at some 200 * point between the given dates, or if dates are equal, currencies in 201 * use on that date. 202 * @param from The date on or after a currency must have been in use. 203 * Measured in milliseconds since Jan 1, 1970 GMT. 204 * @param to The date on or before which a currency must have been in use. 205 * Measured in milliseconds since Jan 1, 1970 GMT. 206 * @stable ICU 51 207 */ onDateRange(long from, long to)208 public static CurrencyFilter onDateRange(long from, long to) { 209 return ALL.withDateRange(from, to); 210 } 211 212 /** 213 * Returns a CurrencyFilter for finding currencies that were either once used, 214 * are used, or will be used as tender. 215 * @stable ICU 51 216 */ onTender()217 public static CurrencyFilter onTender() { 218 return ALL.withTender(); 219 } 220 221 /** 222 * Returns a copy of this filter, with the specified region. Region can be null to 223 * indicate no filter on region. 224 * @param region the region code 225 * @return the filter 226 * @see #onRegion(String) 227 * @stable ICU 4.4 228 */ withRegion(String region)229 public CurrencyFilter withRegion(String region) { 230 return new CurrencyFilter(region, this.currency, this.from, this.to, this.tenderOnly); 231 } 232 233 /** 234 * Returns a copy of this filter, with the specified currency. Currency can be null to 235 * indicate no filter on currency. 236 * @param currency the currency code 237 * @return the filter 238 * @see #onCurrency(String) 239 * @stable ICU 4.4 240 */ withCurrency(String currency)241 public CurrencyFilter withCurrency(String currency) { 242 return new CurrencyFilter(this.region, currency, this.from, this.to, this.tenderOnly); 243 } 244 245 /** 246 * Returns a copy of this filter, with from and to set to the given date. 247 * @param date the date on which the currency must have been in use 248 * @return the filter 249 * @see #onDate(Date) 250 * @stable ICU 4.4 251 */ withDate(Date date)252 public CurrencyFilter withDate(Date date) { 253 return new CurrencyFilter(this.region, this.currency, date.getTime(), date.getTime(), this.tenderOnly); 254 } 255 256 /** 257 * Returns a copy of this filter, with from and to set to the given dates. 258 * @param from date on or after which the currency must have been in use 259 * @param to date on or before which the currency must have been in use 260 * @return the filter 261 * @see #onDateRange(Date, Date) 262 * @stable ICU 49 263 */ withDateRange(Date from, Date to)264 public CurrencyFilter withDateRange(Date from, Date to) { 265 long fromLong = from == null ? Long.MIN_VALUE : from.getTime(); 266 long toLong = to == null ? Long.MAX_VALUE : to.getTime(); 267 return new CurrencyFilter(this.region, this.currency, fromLong, toLong, this.tenderOnly); 268 } 269 270 /** 271 * Returns a copy of this filter that accepts all currencies in use on 272 * the given date. 273 * @param date the date as milliseconds after Jan 1, 1970 274 * @stable ICU 51 275 */ withDate(long date)276 public CurrencyFilter withDate(long date) { 277 return new CurrencyFilter(this.region, this.currency, date, date, this.tenderOnly); 278 } 279 280 /** 281 * Returns a copy of this filter that accepts all currencies that were 282 * in use at some point between the given dates, or if dates are equal, 283 * currencies in use on that date. 284 * @param from The date on or after a currency must have been in use. 285 * Measured in milliseconds since Jan 1, 1970 GMT. 286 * @param to The date on or before which a currency must have been in use. 287 * Measured in milliseconds since Jan 1, 1970 GMT. 288 * @stable ICU 51 289 */ withDateRange(long from, long to)290 public CurrencyFilter withDateRange(long from, long to) { 291 return new CurrencyFilter(this.region, this.currency, from, to, this.tenderOnly); 292 } 293 294 /** 295 * Returns a copy of this filter that filters for currencies that were 296 * either once used, are used, or will be used as tender. 297 * @stable ICU 51 298 */ withTender()299 public CurrencyFilter withTender() { 300 return new CurrencyFilter(this.region, this.currency, this.from, this.to, true); 301 } 302 303 /** 304 * {@inheritDoc} 305 * @stable ICU 4.4 306 */ 307 @Override equals(Object rhs)308 public boolean equals(Object rhs) { 309 return rhs instanceof CurrencyFilter && 310 equals((CurrencyFilter) rhs); 311 } 312 313 /** 314 * Type-safe override of {@link #equals(Object)}. 315 * @param rhs the currency filter to compare to 316 * @return true if the filters are equal 317 * @stable ICU 4.4 318 */ equals(CurrencyFilter rhs)319 public boolean equals(CurrencyFilter rhs) { 320 return this == rhs || (rhs != null && 321 equals(this.region, rhs.region) && 322 equals(this.currency, rhs.currency) && 323 this.from == rhs.from && 324 this.to == rhs.to && 325 this.tenderOnly == rhs.tenderOnly); 326 } 327 328 /** 329 * {@inheritDoc} 330 * @stable ICU 4.4 331 */ 332 @Override hashCode()333 public int hashCode() { 334 int hc = 0; 335 if (region != null) { 336 hc = region.hashCode(); 337 } 338 if (currency != null) { 339 hc = hc * 31 + currency.hashCode(); 340 } 341 hc = hc * 31 + (int) from; 342 hc = hc * 31 + (int) (from >>> 32); 343 hc = hc * 31 + (int) to; 344 hc = hc * 31 + (int) (to >>> 32); 345 hc = hc * 31 + (tenderOnly ? 1 : 0); 346 return hc; 347 } 348 349 /** 350 * Returns a string representing the filter, for debugging. 351 * @return A string representing the filter. 352 * @stable ICU 4.4 353 */ 354 @Override toString()355 public String toString() { 356 return debugString(this); 357 } 358 equals(String lhs, String rhs)359 private static boolean equals(String lhs, String rhs) { 360 return lhs == rhs || (lhs != null && lhs.equals(rhs)); 361 } 362 } 363 364 /** 365 * Represents the raw information about fraction digits and rounding increment. 366 * @stable ICU 4.4 367 */ 368 public static final class CurrencyDigits { 369 /** 370 * Number of fraction digits used to display this currency. 371 * @stable ICU 49 372 */ 373 public final int fractionDigits; 374 /** 375 * Rounding increment used when displaying this currency. 376 * @stable ICU 49 377 */ 378 public final int roundingIncrement; 379 380 /** 381 * Constructor for CurrencyDigits. 382 * @param fractionDigits the fraction digits 383 * @param roundingIncrement the rounding increment 384 * @stable ICU 4.4 385 */ CurrencyDigits(int fractionDigits, int roundingIncrement)386 public CurrencyDigits(int fractionDigits, int roundingIncrement) { 387 this.fractionDigits = fractionDigits; 388 this.roundingIncrement = roundingIncrement; 389 } 390 391 /** 392 * Returns a string representing the currency digits, for debugging. 393 * @return A string representing the currency digits. 394 * @stable ICU 4.4 395 */ 396 @Override toString()397 public String toString() { 398 return debugString(this); 399 } 400 } 401 402 /** 403 * Represents a complete currency info record listing the region, currency, from and to dates, 404 * and priority. 405 * Use {@link CurrencyMetaInfo#currencyInfo(CurrencyFilter)} 406 * for a list of info objects matching the filter. 407 * @stable ICU 4.4 408 */ 409 public static final class CurrencyInfo { 410 /** 411 * Region code where currency is used. 412 * @stable ICU 4.4 413 */ 414 public final String region; 415 416 /** 417 * The three-letter ISO currency code. 418 * @stable ICU 4.4 419 */ 420 public final String code; 421 422 /** 423 * Date on which the currency was first officially used in the region. 424 * This is midnight at the start of the first day on which the currency was used, GMT. 425 * If there is no date, this is Long.MIN_VALUE; 426 * @stable ICU 4.4 427 */ 428 public final long from; 429 430 /** 431 * Date at which the currency stopped being officially used in the region. 432 * This is one millisecond before midnight at the end of the last day on which the currency was used, GMT. 433 * If there is no date, this is Long.MAX_VALUE. 434 * 435 * @stable ICU 4.4 436 */ 437 public final long to; 438 439 /** 440 * Preference order of currencies being used at the same time in the region. Lower 441 * values are preferred (generally, this is a transition from an older to a newer 442 * currency). Priorities within a single country are unique. 443 * @stable ICU 49 444 */ 445 public final int priority; 446 447 448 private final boolean tender; 449 450 /** 451 * @deprecated ICU 51 Use {@link CurrencyMetaInfo#currencyInfo(CurrencyFilter)} instead. 452 */ 453 @Deprecated CurrencyInfo(String region, String code, long from, long to, int priority)454 public CurrencyInfo(String region, String code, long from, long to, int priority) { 455 this(region, code, from, to, priority, true); 456 } 457 458 /** 459 * Constructs a currency info. 460 * 461 * @internal 462 * @deprecated This API is ICU internal only. 463 */ 464 @Deprecated CurrencyInfo(String region, String code, long from, long to, int priority, boolean tender)465 public CurrencyInfo(String region, String code, long from, long to, int priority, boolean tender) { 466 this.region = region; 467 this.code = code; 468 this.from = from; 469 this.to = to; 470 this.priority = priority; 471 this.tender = tender; 472 } 473 474 /** 475 * Returns a string representation of this object, useful for debugging. 476 * @return A string representation of this object. 477 * @stable ICU 4.4 478 */ 479 @Override toString()480 public String toString() { 481 return debugString(this); 482 } 483 484 /** 485 * Determine whether or not this currency was once used, is used, 486 * or will be used as tender in this region. 487 * @stable ICU 51 488 */ isTender()489 public boolean isTender() { 490 return tender; 491 } 492 } 493 494 ///CLOVER:OFF 495 /** 496 * Returns the list of CurrencyInfos matching the provided filter. Results 497 * are ordered by country code, then by highest to lowest priority (0 is highest). 498 * The returned list is unmodifiable. 499 * @param filter the filter to control which currency info to return 500 * @return the matching information 501 * @stable ICU 4.4 502 */ currencyInfo(CurrencyFilter filter)503 public List<CurrencyInfo> currencyInfo(CurrencyFilter filter) { 504 return Collections.emptyList(); 505 } 506 507 /** 508 * Returns the list of currency codes matching the provided filter. 509 * Results are ordered as in {@link #currencyInfo(CurrencyFilter)}. 510 * The returned list is unmodifiable. 511 * @param filter the filter to control which currencies to return. If filter is null, 512 * returns all currencies for which information is available. 513 * @return the matching currency codes 514 * @stable ICU 4.4 515 */ currencies(CurrencyFilter filter)516 public List<String> currencies(CurrencyFilter filter) { 517 return Collections.emptyList(); 518 } 519 520 /** 521 * Returns the list of region codes matching the provided filter. 522 * Results are ordered as in {@link #currencyInfo(CurrencyFilter)}. 523 * The returned list is unmodifiable. 524 * @param filter the filter to control which regions to return. If filter is null, 525 * returns all regions for which information is available. 526 * @return the matching region codes 527 * @stable ICU 4.4 528 */ regions(CurrencyFilter filter)529 public List<String> regions(CurrencyFilter filter) { 530 return Collections.emptyList(); 531 } 532 ///CLOVER:ON 533 534 /** 535 * Returns the CurrencyDigits for the currency code. 536 * This is equivalent to currencyDigits(isoCode, CurrencyUsage.STANDARD); 537 * @param isoCode the currency code 538 * @return the CurrencyDigits 539 * @stable ICU 4.4 540 */ currencyDigits(String isoCode)541 public CurrencyDigits currencyDigits(String isoCode) { 542 return currencyDigits(isoCode, CurrencyUsage.STANDARD); 543 } 544 545 /** 546 * Returns the CurrencyDigits for the currency code with Context Usage. 547 * @param isoCode the currency code 548 * @param currencyUsage the currency usage 549 * @return the CurrencyDigits 550 * @draft ICU 54 551 * @provisional This API might change or be removed in a future release. 552 */ currencyDigits(String isoCode, CurrencyUsage currencyUsage)553 public CurrencyDigits currencyDigits(String isoCode, CurrencyUsage currencyUsage) { 554 return defaultDigits; 555 } 556 557 /** 558 * @internal 559 * @deprecated This API is ICU internal only. 560 */ 561 @Deprecated 562 protected static final CurrencyDigits defaultDigits = new CurrencyDigits(2, 0); 563 564 static { 565 CurrencyMetaInfo temp = null; 566 boolean tempHasData = false; 567 try { 568 Class<?> clzz = Class.forName("com.ibm.icu.impl.ICUCurrencyMetaInfo"); 569 temp = (CurrencyMetaInfo) clzz.newInstance(); 570 tempHasData = true; 571 } catch (Throwable t) { 572 temp = new CurrencyMetaInfo(); 573 } 574 impl = temp; 575 hasData = tempHasData; 576 } 577 dateString(long date)578 private static String dateString(long date) { 579 if (date == Long.MAX_VALUE || date == Long.MIN_VALUE) { 580 return null; 581 } 582 return Grego.timeToString(date); 583 } 584 debugString(Object o)585 private static String debugString(Object o) { 586 StringBuilder sb = new StringBuilder(); 587 try { 588 for (Field f : o.getClass().getFields()) { 589 Object v = f.get(o); 590 if (v != null) { 591 String s; 592 if (v instanceof Date) { 593 s = dateString(((Date)v).getTime()); 594 } else if (v instanceof Long) { 595 s = dateString(((Long)v).longValue()); 596 } else { 597 s = String.valueOf(v); 598 } 599 if (s == null) { 600 continue; 601 } 602 if (sb.length() > 0) { 603 sb.append(","); 604 } 605 sb.append(f.getName()) 606 .append("='") 607 .append(s) 608 .append("'"); 609 } 610 } 611 } catch (Throwable t) { 612 } 613 sb.insert(0, o.getClass().getSimpleName() + "("); 614 sb.append(")"); 615 return sb.toString(); 616 } 617 } 618