17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 2009-2014, International Business Machines Corporation and    *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.CurrencyMetaInfo;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Currency.CurrencyUsage;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * ICU's currency meta info data.
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class ICUCurrencyMetaInfo extends CurrencyMetaInfo {
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ICUResourceBundle regionInfo;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ICUResourceBundle digitInfo;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ICUCurrencyMetaInfo() {
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle.ICU_CURR_BASE_NAME, "supplementalData",
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle.ICU_DATA_CLASS_LOADER);
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        regionInfo = bundle.findTopLevel("CurrencyMap");
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        digitInfo = bundle.findTopLevel("CurrencyMeta");
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public List<CurrencyInfo> currencyInfo(CurrencyFilter filter) {
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return collect(new InfoCollector(), filter);
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public List<String> currencies(CurrencyFilter filter) {
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return collect(new CurrencyCollector(), filter);
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   }
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public List<String> regions(CurrencyFilter filter) {
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return collect(new RegionCollector(), filter);
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public CurrencyDigits currencyDigits(String isoCode) {
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return currencyDigits(isoCode, CurrencyUsage.STANDARD);
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public CurrencyDigits currencyDigits(String isoCode, CurrencyUsage currencyPurpose) {
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ICUResourceBundle b = digitInfo.findWithFallback(isoCode);
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (b == null) {
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            b = digitInfo.findWithFallback("DEFAULT");
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] data = b.getIntVector();
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (currencyPurpose == CurrencyUsage.CASH) {
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new CurrencyDigits(data[2], data[3]);
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if (currencyPurpose == CurrencyUsage.STANDARD) {
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new CurrencyDigits(data[0], data[1]);
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new CurrencyDigits(data[0], data[1]);
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private <T> List<T> collect(Collector<T> collector, CurrencyFilter filter) {
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We rely on the fact that the data lists the regions in order, and the
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // priorities in order within region.  This means we don't need
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // to sort the results to ensure the ordering matches the spec.
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (filter == null) {
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            filter = CurrencyFilter.all();
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int needed = collector.collects();
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (filter.region != null) {
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needed |= Region;
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (filter.currency != null) {
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needed |= Currency;
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (filter.from != Long.MIN_VALUE || filter.to != Long.MAX_VALUE) {
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needed |= Date;
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (filter.tenderOnly) {
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            needed |= Tender;
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (needed != 0) {
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (filter.region != null) {
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle b = regionInfo.findWithFallback(filter.region);
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (b != null) {
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    collectRegion(collector, filter, needed, b);
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < regionInfo.getSize(); i++) {
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    collectRegion(collector, filter, needed, regionInfo.at(i));
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return collector.getList();
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private <T> void collectRegion(Collector<T> collector, CurrencyFilter filter,
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int needed, ICUResourceBundle b) {
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String region = b.getKey();
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (needed == Region) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            collector.collect(b.getKey(), null, 0, 0, -1, false);
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < b.getSize(); i++) {
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ICUResourceBundle r = b.at(i);
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (r.getSize() == 0) {
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // AQ[0] is an empty array instead of a table, so the bundle is null.
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // There's no data here, so we skip this entirely.
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // We'd do a type test, but the ResourceArray type is private.
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String currency = null;
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long from = Long.MIN_VALUE;
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long to = Long.MAX_VALUE;
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean tender = true;
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((needed & Currency) != 0) {
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle currBundle = r.at("id");
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                currency = currBundle.getString();
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (filter.currency != null && !filter.currency.equals(currency)) {
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((needed & Date) != 0) {
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                from = getDate(r.at("from"), Long.MIN_VALUE, false);
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                to = getDate(r.at("to"), Long.MAX_VALUE, true);
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // In the data, to is always > from.  This means that when we have a range
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // from == to, the comparisons below will always do the right thing, despite
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // the range being technically empty.  It really should be [from, from+1) but
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // this way we don't need to fiddle with it.
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (filter.from > to) {
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (filter.to < from) {
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((needed & Tender) != 0) {
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle tenderBundle = r.at("tender");
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tender = tenderBundle == null || "true".equals(tenderBundle.getString());
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (filter.tenderOnly && !tender) {
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // data lists elements in priority order, so 'i' suffices
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            collector.collect(region, currency, from, to, i, tender);
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long MASK = 4294967295L;
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private long getDate(ICUResourceBundle b, long defaultValue, boolean endOfDay) {
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (b == null) {
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return defaultValue;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] values = b.getIntVector();
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ((long) values[0] << 32) | (((long) values[1]) & MASK);
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Utility, just because I don't like the n^2 behavior of using list.contains to build a
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // list of unique items.  If we used java 6 we could use their class for this.
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class UniqueList<T> {
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private Set<T> seen = new HashSet<T>();
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private List<T> list = new ArrayList<T>();
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static <T> UniqueList<T> create() {
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new UniqueList<T>();
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void add(T value) {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!seen.contains(value)) {
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                list.add(value);
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                seen.add(value);
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<T> list() {
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collections.unmodifiableList(list);
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class InfoCollector implements Collector<CurrencyInfo> {
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Data is already unique by region/priority, so we don't need to be concerned
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // about duplicates.
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private List<CurrencyInfo> result = new ArrayList<CurrencyInfo>();
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void collect(String region, String currency, long from, long to, int priority, boolean tender) {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(new CurrencyInfo(region, currency, from, to, priority, tender));
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public List<CurrencyInfo> getList() {
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collections.unmodifiableList(result);
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int collects() {
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Everything;
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class RegionCollector implements Collector<String> {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final UniqueList<String> result = UniqueList.create();
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void collect(
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String region, String currency, long from, long to, int priority, boolean tender) {
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(region);
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int collects() {
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Region;
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public List<String> getList() {
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.list();
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static class CurrencyCollector implements Collector<String> {
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final UniqueList<String> result = UniqueList.create();
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void collect(
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String region, String currency, long from, long to, int priority, boolean tender) {
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.add(currency);
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int collects() {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Currency;
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public List<String> getList() {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result.list();
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int Region = 1;
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int Currency = 2;
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int Date = 4;
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int Tender = 8;
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int Everything = Integer.MAX_VALUE;
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static interface Collector<T> {
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * A bitmask of Region/Currency/Date indicating which features we collect.
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return the bitmask
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int collects();
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Called with data passed by filter.  Values not collected by filter should be ignored.
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param region the region code (null if ignored)
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param currency the currency code (null if ignored)
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param from start time (0 if ignored)
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param to end time (0 if ignored)
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param priority priority (-1 if ignored)
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param tender true if currency is legal tender.
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        void collect(String region, String currency, long from, long to, int priority, boolean tender);
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the list of unique items in the order in which we encountered them for the
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * first time.  The returned list is unmodifiable.
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return the list
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<T> getList();
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
279