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