2 *******************************************************************************
3 * Copyright (C) 2009-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.HashSet;
12 import java.util.List;
15 import com.ibm.icu.text.CurrencyMetaInfo;
18 * ICU's currency meta info data.
20 public class ICUCurrencyMetaInfo extends CurrencyMetaInfo {
21 private ICUResourceBundle regionInfo;
22 private ICUResourceBundle digitInfo;
24 public ICUCurrencyMetaInfo() {
25 ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
26 ICUResourceBundle.ICU_CURR_BASE_NAME, "supplementalData",
27 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
28 regionInfo = bundle.findTopLevel("CurrencyMap");
29 digitInfo = bundle.findTopLevel("CurrencyMeta");
33 public List<CurrencyInfo> currencyInfo(CurrencyFilter filter) {
34 return collect(new InfoCollector(), filter);
38 public List<String> currencies(CurrencyFilter filter) {
39 return collect(new CurrencyCollector(), filter);
43 public List<String> regions(CurrencyFilter filter) {
44 return collect(new RegionCollector(), filter);
48 public CurrencyDigits currencyDigits(String isoCode) {
49 ICUResourceBundle b = digitInfo.findWithFallback(isoCode);
51 b = digitInfo.findWithFallback("DEFAULT");
53 int[] data = b.getIntVector();
54 return new CurrencyDigits(data[0], data[1]);
57 private <T> List<T> collect(Collector<T> collector, CurrencyFilter filter) {
58 // We rely on the fact that the data lists the regions in order, and the
59 // priorities in order within region. This means we don't need
60 // to sort the results to ensure the ordering matches the spec.
63 filter = CurrencyFilter.all();
65 int needed = collector.collects();
66 if (filter.region != null) {
69 if (filter.currency != null) {
72 if (filter.from != Long.MIN_VALUE || filter.to != Long.MAX_VALUE) {
75 if (filter.tenderOnly) {
80 if (filter.region != null) {
81 ICUResourceBundle b = regionInfo.findWithFallback(filter.region);
83 collectRegion(collector, filter, needed, b);
86 for (int i = 0; i < regionInfo.getSize(); i++) {
87 collectRegion(collector, filter, needed, regionInfo.at(i));
92 return collector.getList();
95 private <T> void collectRegion(Collector<T> collector, CurrencyFilter filter,
96 int needed, ICUResourceBundle b) {
98 String region = b.getKey();
99 if (needed == Region) {
100 collector.collect(b.getKey(), null, 0, 0, -1, false);
104 for (int i = 0; i < b.getSize(); i++) {
105 ICUResourceBundle r = b.at(i);
106 if (r.getSize() == 0) {
107 // AQ[0] is an empty array instead of a table, so the bundle is null.
108 // There's no data here, so we skip this entirely.
109 // We'd do a type test, but the ResourceArray type is private.
112 String currency = null;
113 long from = Long.MIN_VALUE;
114 long to = Long.MAX_VALUE;
115 boolean tender = true;
117 if ((needed & Currency) != 0) {
118 ICUResourceBundle currBundle = r.at("id");
119 currency = currBundle.getString();
120 if (filter.currency != null && !filter.currency.equals(currency)) {
125 if ((needed & Date) != 0) {
126 from = getDate(r.at("from"), Long.MIN_VALUE, false);
127 to = getDate(r.at("to"), Long.MAX_VALUE, true);
128 // In the data, to is always > from. This means that when we have a range
129 // from == to, the comparisons below will always do the right thing, despite
130 // the range being technically empty. It really should be [from, from+1) but
131 // this way we don't need to fiddle with it.
132 if (filter.from > to) {
135 if (filter.to < from) {
139 if ((needed & Tender) != 0) {
140 ICUResourceBundle tenderBundle = r.at("tender");
141 tender = tenderBundle == null || "true".equals(tenderBundle.getString());
142 if (filter.tenderOnly && !tender) {
147 // data lists elements in priority order, so 'i' suffices
148 collector.collect(region, currency, from, to, i, tender);
152 private static final long MASK = 4294967295L;
153 private long getDate(ICUResourceBundle b, long defaultValue, boolean endOfDay) {
157 int[] values = b.getIntVector();
158 return ((long) values[0] << 32) | (((long) values[1]) & MASK);
161 // Utility, just because I don't like the n^2 behavior of using list.contains to build a
162 // list of unique items. If we used java 6 we could use their class for this.
163 private static class UniqueList<T> {
164 private Set<T> seen = new HashSet<T>();
165 private List<T> list = new ArrayList<T>();
167 private static <T> UniqueList<T> create() {
168 return new UniqueList<T>();
172 if (!seen.contains(value)) {
179 return Collections.unmodifiableList(list);
183 private static class InfoCollector implements Collector<CurrencyInfo> {
184 // Data is already unique by region/priority, so we don't need to be concerned
186 private List<CurrencyInfo> result = new ArrayList<CurrencyInfo>();
188 public void collect(String region, String currency, long from, long to, int priority, boolean tender) {
189 result.add(new CurrencyInfo(region, currency, from, to, priority, tender));
192 public List<CurrencyInfo> getList() {
193 return Collections.unmodifiableList(result);
196 public int collects() {
201 private static class RegionCollector implements Collector<String> {
202 private final UniqueList<String> result = UniqueList.create();
205 String region, String currency, long from, long to, int priority, boolean tender) {
209 public int collects() {
213 public List<String> getList() {
214 return result.list();
218 private static class CurrencyCollector implements Collector<String> {
219 private final UniqueList<String> result = UniqueList.create();
222 String region, String currency, long from, long to, int priority, boolean tender) {
223 result.add(currency);
226 public int collects() {
230 public List<String> getList() {
231 return result.list();
235 private static final int Region = 1;
236 private static final int Currency = 2;
237 private static final int Date = 4;
238 private static final int Tender = 8;
239 private static final int Everything = Integer.MAX_VALUE;
241 private static interface Collector<T> {
243 * A bitmask of Region/Currency/Date indicating which features we collect.
244 * @return the bitmask
249 * Called with data passed by filter. Values not collected by filter should be ignored.
250 * @param region the region code (null if ignored)
251 * @param currency the currency code (null if ignored)
252 * @param from start time (0 if ignored)
253 * @param to end time (0 if ignored)
254 * @param priority priority (-1 if ignored)
255 * @param tender true if currency is legal tender.
257 void collect(String region, String currency, long from, long to, int priority, boolean tender);
260 * Return the list of unique items in the order in which we encountered them for the
261 * first time. The returned list is unmodifiable.