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