com.ibm.icu.impl.ICUCurrencyMetaInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
/*
*******************************************************************************
* Copyright (C) 2009-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.ibm.icu.text.CurrencyMetaInfo;
import com.ibm.icu.util.Currency.CurrencyUsage;
/**
* ICU's currency meta info data.
*/
public class ICUCurrencyMetaInfo extends CurrencyMetaInfo {
private ICUResourceBundle regionInfo;
private ICUResourceBundle digitInfo;
public ICUCurrencyMetaInfo() {
ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
ICUResourceBundle.ICU_CURR_BASE_NAME, "supplementalData",
ICUResourceBundle.ICU_DATA_CLASS_LOADER);
regionInfo = bundle.findTopLevel("CurrencyMap");
digitInfo = bundle.findTopLevel("CurrencyMeta");
}
@Override
public List currencyInfo(CurrencyFilter filter) {
return collect(new InfoCollector(), filter);
}
@Override
public List currencies(CurrencyFilter filter) {
return collect(new CurrencyCollector(), filter);
}
@Override
public List regions(CurrencyFilter filter) {
return collect(new RegionCollector(), filter);
}
@Override
public CurrencyDigits currencyDigits(String isoCode) {
return currencyDigits(isoCode, CurrencyUsage.STANDARD);
}
@Override
public CurrencyDigits currencyDigits(String isoCode, CurrencyUsage currencyPurpose) {
ICUResourceBundle b = digitInfo.findWithFallback(isoCode);
if (b == null) {
b = digitInfo.findWithFallback("DEFAULT");
}
int[] data = b.getIntVector();
if (currencyPurpose == CurrencyUsage.CASH) {
return new CurrencyDigits(data[2], data[3]);
} else if (currencyPurpose == CurrencyUsage.STANDARD) {
return new CurrencyDigits(data[0], data[1]);
} else {
return new CurrencyDigits(data[0], data[1]);
}
}
private List collect(Collector collector, CurrencyFilter filter) {
// We rely on the fact that the data lists the regions in order, and the
// priorities in order within region. This means we don't need
// to sort the results to ensure the ordering matches the spec.
if (filter == null) {
filter = CurrencyFilter.all();
}
int needed = collector.collects();
if (filter.region != null) {
needed |= Region;
}
if (filter.currency != null) {
needed |= Currency;
}
if (filter.from != Long.MIN_VALUE || filter.to != Long.MAX_VALUE) {
needed |= Date;
}
if (filter.tenderOnly) {
needed |= Tender;
}
if (needed != 0) {
if (filter.region != null) {
ICUResourceBundle b = regionInfo.findWithFallback(filter.region);
if (b != null) {
collectRegion(collector, filter, needed, b);
}
} else {
for (int i = 0; i < regionInfo.getSize(); i++) {
collectRegion(collector, filter, needed, regionInfo.at(i));
}
}
}
return collector.getList();
}
private void collectRegion(Collector collector, CurrencyFilter filter,
int needed, ICUResourceBundle b) {
String region = b.getKey();
if (needed == Region) {
collector.collect(b.getKey(), null, 0, 0, -1, false);
return;
}
for (int i = 0; i < b.getSize(); i++) {
ICUResourceBundle r = b.at(i);
if (r.getSize() == 0) {
// AQ[0] is an empty array instead of a table, so the bundle is null.
// There's no data here, so we skip this entirely.
// We'd do a type test, but the ResourceArray type is private.
continue;
}
String currency = null;
long from = Long.MIN_VALUE;
long to = Long.MAX_VALUE;
boolean tender = true;
if ((needed & Currency) != 0) {
ICUResourceBundle currBundle = r.at("id");
currency = currBundle.getString();
if (filter.currency != null && !filter.currency.equals(currency)) {
continue;
}
}
if ((needed & Date) != 0) {
from = getDate(r.at("from"), Long.MIN_VALUE, false);
to = getDate(r.at("to"), Long.MAX_VALUE, true);
// In the data, to is always > from. This means that when we have a range
// from == to, the comparisons below will always do the right thing, despite
// the range being technically empty. It really should be [from, from+1) but
// this way we don't need to fiddle with it.
if (filter.from > to) {
continue;
}
if (filter.to < from) {
continue;
}
}
if ((needed & Tender) != 0) {
ICUResourceBundle tenderBundle = r.at("tender");
tender = tenderBundle == null || "true".equals(tenderBundle.getString());
if (filter.tenderOnly && !tender) {
continue;
}
}
// data lists elements in priority order, so 'i' suffices
collector.collect(region, currency, from, to, i, tender);
}
}
private static final long MASK = 4294967295L;
private long getDate(ICUResourceBundle b, long defaultValue, boolean endOfDay) {
if (b == null) {
return defaultValue;
}
int[] values = b.getIntVector();
return ((long) values[0] << 32) | (((long) values[1]) & MASK);
}
// Utility, just because I don't like the n^2 behavior of using list.contains to build a
// list of unique items. If we used java 6 we could use their class for this.
private static class UniqueList {
private Set seen = new HashSet();
private List list = new ArrayList();
private static UniqueList create() {
return new UniqueList();
}
void add(T value) {
if (!seen.contains(value)) {
list.add(value);
seen.add(value);
}
}
List list() {
return Collections.unmodifiableList(list);
}
}
private static class InfoCollector implements Collector {
// Data is already unique by region/priority, so we don't need to be concerned
// about duplicates.
private List result = new ArrayList();
public void collect(String region, String currency, long from, long to, int priority, boolean tender) {
result.add(new CurrencyInfo(region, currency, from, to, priority, tender));
}
public List getList() {
return Collections.unmodifiableList(result);
}
public int collects() {
return Everything;
}
}
private static class RegionCollector implements Collector {
private final UniqueList result = UniqueList.create();
public void collect(
String region, String currency, long from, long to, int priority, boolean tender) {
result.add(region);
}
public int collects() {
return Region;
}
public List getList() {
return result.list();
}
}
private static class CurrencyCollector implements Collector {
private final UniqueList result = UniqueList.create();
public void collect(
String region, String currency, long from, long to, int priority, boolean tender) {
result.add(currency);
}
public int collects() {
return Currency;
}
public List getList() {
return result.list();
}
}
private static final int Region = 1;
private static final int Currency = 2;
private static final int Date = 4;
private static final int Tender = 8;
private static final int Everything = Integer.MAX_VALUE;
private static interface Collector {
/**
* A bitmask of Region/Currency/Date indicating which features we collect.
* @return the bitmask
*/
int collects();
/**
* Called with data passed by filter. Values not collected by filter should be ignored.
* @param region the region code (null if ignored)
* @param currency the currency code (null if ignored)
* @param from start time (0 if ignored)
* @param to end time (0 if ignored)
* @param priority priority (-1 if ignored)
* @param tender true if currency is legal tender.
*/
void collect(String region, String currency, long from, long to, int priority, boolean tender);
/**
* Return the list of unique items in the order in which we encountered them for the
* first time. The returned list is unmodifiable.
* @return the list
*/
List getList();
}
}