2 *******************************************************************************
\r
3 * Copyright (C) 2009-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.impl;
\r
9 import java.util.ArrayList;
\r
10 import java.util.Collections;
\r
11 import java.util.HashSet;
\r
12 import java.util.List;
\r
13 import java.util.Set;
\r
15 import com.ibm.icu.text.CurrencyMetaInfo;
\r
18 * ICU's currency meta info data.
\r
20 public class ICUCurrencyMetaInfo extends CurrencyMetaInfo {
\r
21 private ICUResourceBundle regionInfo;
\r
22 private ICUResourceBundle digitInfo;
\r
24 public ICUCurrencyMetaInfo() {
\r
25 ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
\r
26 ICUResourceBundle.ICU_CURR_BASE_NAME, "supplementalData",
\r
27 ICUResourceBundle.ICU_DATA_CLASS_LOADER);
\r
28 regionInfo = bundle.findTopLevel("CurrencyMap");
\r
29 digitInfo = bundle.findTopLevel("CurrencyMeta");
\r
33 public List<CurrencyInfo> currencyInfo(CurrencyFilter filter) {
\r
34 return collect(new InfoCollector(), filter);
\r
38 public List<String> currencies(CurrencyFilter filter) {
\r
39 return collect(new CurrencyCollector(), filter);
\r
43 public List<String> regions(CurrencyFilter filter) {
\r
44 return collect(new RegionCollector(), filter);
\r
48 public CurrencyDigits currencyDigits(String isoCode) {
\r
49 ICUResourceBundle b = digitInfo.findWithFallback(isoCode);
\r
51 b = digitInfo.findWithFallback("DEFAULT");
\r
53 int[] data = b.getIntVector();
\r
54 return new CurrencyDigits(data[0], data[1]);
\r
57 private <T> List<T> collect(Collector<T> collector, CurrencyFilter filter) {
\r
58 // We rely on the fact that the data lists the regions in order, and the
\r
59 // priorities in order within region. This means we don't need
\r
60 // to sort the results to ensure the ordering matches the spec.
\r
62 if (filter == null) {
\r
63 filter = CurrencyFilter.all();
\r
65 int needed = collector.collects();
\r
66 if (filter.region != null) {
\r
69 if (filter.currency != null) {
\r
72 if (filter.from != Long.MIN_VALUE || filter.to != Long.MAX_VALUE) {
\r
77 if (filter.region != null) {
\r
78 ICUResourceBundle b = regionInfo.findWithFallback(filter.region);
\r
80 collectRegion(collector, filter, needed, b);
\r
83 for (int i = 0; i < regionInfo.getSize(); i++) {
\r
84 collectRegion(collector, filter, needed, regionInfo.at(i));
\r
89 return collector.getList();
\r
92 private <T> void collectRegion(Collector<T> collector, CurrencyFilter filter,
\r
93 int needed, ICUResourceBundle b) {
\r
95 String region = b.getKey();
\r
96 if ((needed & nonRegion) == 0) {
\r
97 collector.collect(b.getKey(), null, 0, 0, -1);
\r
101 for (int i = 0; i < b.getSize(); i++) {
\r
102 ICUResourceBundle r = b.at(i);
\r
103 if (r.getSize() == 0) {
\r
104 // AQ[0] is an empty array instead of a table, so the bundle is null.
\r
105 // There's no data here, so we skip this entirely.
\r
106 // We'd do a type test, but the ResourceArray type is private.
\r
109 String currency = null;
\r
110 long from = Long.MIN_VALUE;
\r
111 long to = Long.MAX_VALUE;
\r
113 if ((needed & Currency) != 0) {
\r
114 ICUResourceBundle currBundle = r.at("id");
\r
115 currency = currBundle.getString();
\r
116 if (filter.currency != null && !filter.currency.equals(currency)) {
\r
121 if ((needed & Date) != 0) {
\r
122 from = getDate(r.at("from"), Long.MIN_VALUE);
\r
123 to = getDate(r.at("to"), Long.MAX_VALUE);
\r
124 // In the data, to is always > from. This means that when we have a range
\r
125 // from == to, the comparisons below will always do the right thing, despite
\r
126 // the range being technically empty. It really should be [from, from+1) but
\r
127 // this way we don't need to fiddle with it.
\r
128 if (filter.from >= to) {
\r
131 if (filter.to <= from) {
\r
136 // data lists elements in priority order, so 'i' suffices
\r
137 collector.collect(region, currency, from, to, i);
\r
141 private static final long MASK = 4294967295L;
\r
142 private long getDate(ICUResourceBundle b, long defaultValue) {
\r
144 return defaultValue;
\r
146 int[] values = b.getIntVector();
\r
147 return ((long) values[0] << 32) | ((long) values[1] & MASK);
\r
150 // Utility, just because I don't like the n^2 behavior of using list.contains to build a
\r
151 // list of unique items. If we used java 6 we could use their class for this.
\r
152 private static class UniqueList<T> {
\r
153 private Set<T> seen = new HashSet<T>();
\r
154 private List<T> list = new ArrayList<T>();
\r
156 private static <T> UniqueList<T> create() {
\r
157 return new UniqueList<T>();
\r
160 void add(T value) {
\r
161 if (!seen.contains(value)) {
\r
168 return Collections.unmodifiableList(list);
\r
172 private static class InfoCollector implements Collector<CurrencyInfo> {
\r
173 // Data is already unique by region/priority, so we don't need to be concerned
\r
174 // about duplicates.
\r
175 private List<CurrencyInfo> result = new ArrayList<CurrencyInfo>();
\r
177 public void collect(String region, String currency, long from, long to, int priority) {
\r
178 result.add(new CurrencyInfo(region, currency, from, to, priority));
\r
181 public List<CurrencyInfo> getList() {
\r
182 return Collections.unmodifiableList(result);
\r
185 public int collects() {
\r
186 return Region | Currency | Date;
\r
190 private static class RegionCollector implements Collector<String> {
\r
191 private final UniqueList<String> result = UniqueList.create();
\r
193 public void collect(String region, String currency, long from, long to, int priority) {
\r
194 result.add(region);
\r
197 public int collects() {
\r
201 public List<String> getList() {
\r
202 return result.list();
\r
206 private static class CurrencyCollector implements Collector<String> {
\r
207 private final UniqueList<String> result = UniqueList.create();
\r
209 public void collect(String region, String currency, long from, long to, int priority) {
\r
210 result.add(currency);
\r
213 public int collects() {
\r
217 public List<String> getList() {
\r
218 return result.list();
\r
222 private static final int Region = 1;
\r
223 private static final int Currency = 2;
\r
224 private static final int Date = 4;
\r
226 private static final int nonRegion = Currency | Date;
\r
228 private static interface Collector<T> {
\r
230 * A bitmask of Region/Currency/Date indicating which features we collect.
\r
231 * @return the bitmask
\r
236 * Called with data passed by filter. Values not collected by filter should be ignored.
\r
237 * @param region the region code (null if ignored)
\r
238 * @param currency the currency code (null if ignored)
\r
239 * @param from start time (0 if ignored)
\r
240 * @param to end time (0 if ignored)
\r
241 * @param priority priority (-1 if ignored)
\r
243 void collect(String region, String currency, long from, long to, int priority);
\r
246 * Return the list of unique items in the order in which we encountered them for the
\r
247 * first time. The returned list is unmodifiable.
\r