ICU-13368 Fixing performance regression on single-symbol currency data loading. Adding single-element data caches to ICUCurrencyDisplayInfoProvider.

X-SVN-Rev: 40561
This commit is contained in:
Shane Carr 2017-10-05 01:19:20 +00:00
parent 25fed96c72
commit e7cfe897b2
2 changed files with 341 additions and 201 deletions

View File

@ -33,12 +33,14 @@ public class CurrencyData {
}
public static final class CurrencyFormatInfo {
public final String isoCode;
public final String currencyPattern;
public final String monetaryDecimalSeparator;
public final String monetaryGroupingSeparator;
public CurrencyFormatInfo(String currencyPattern, String monetarySeparator,
public CurrencyFormatInfo(String isoCode, String currencyPattern, String monetarySeparator,
String monetaryGroupingSeparator) {
this.isoCode = isoCode;
this.currencyPattern = currencyPattern;
this.monetaryDecimalSeparator = monetarySeparator;
this.monetaryGroupingSeparator = monetaryGroupingSeparator;

View File

@ -9,7 +9,6 @@
package com.ibm.icu.impl;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
@ -27,21 +26,31 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
public ICUCurrencyDisplayInfoProvider() {
}
/**
* Single-item cache for ICUCurrencyDisplayInfo keyed by locale.
*/
private volatile ICUCurrencyDisplayInfo currencyDisplayInfoCache = null;
@Override
public CurrencyDisplayInfo getInstance(ULocale locale, boolean withFallback) {
ICUResourceBundle rb;
if (withFallback) {
rb = ICUResourceBundle.getBundleInstance(
ICUData.ICU_CURR_BASE_NAME, locale, OpenType.LOCALE_DEFAULT_ROOT);
} else {
try {
ICUCurrencyDisplayInfo instance = currencyDisplayInfoCache;
if (instance == null || !instance.locale.equals(locale) || instance.fallback != withFallback) {
ICUResourceBundle rb;
if (withFallback) {
rb = ICUResourceBundle.getBundleInstance(
ICUData.ICU_CURR_BASE_NAME, locale, OpenType.LOCALE_ONLY);
} catch (MissingResourceException e) {
return null;
ICUData.ICU_CURR_BASE_NAME, locale, OpenType.LOCALE_DEFAULT_ROOT);
} else {
try {
rb = ICUResourceBundle.getBundleInstance(
ICUData.ICU_CURR_BASE_NAME, locale, OpenType.LOCALE_ONLY);
} catch (MissingResourceException e) {
return null;
}
}
instance = new ICUCurrencyDisplayInfo(locale, rb, withFallback);
currencyDisplayInfoCache = instance;
}
return new ICUCurrencyDisplayInfo(rb, withFallback);
return instance;
}
@Override
@ -49,70 +58,82 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
return true;
}
/**
* This class performs data loading for currencies and keeps data in lightweight cache.
*/
static class ICUCurrencyDisplayInfo extends CurrencyDisplayInfo {
private final boolean fallback;
final ULocale locale;
final boolean fallback;
private final ICUResourceBundle rb;
private volatile SoftReference<RawCurrencyData> rawDataCache;
/**
* The primary data structure is isoCodeToCurrencyStrings. In that structure,
* the String arrays contain the following elements:
*
* [DISPLAY_NAME] => display name
* [SYMBOL] => symbol
* [NARROW_SYMBOL] => narrow symbol
* [FORMAT_PATTERN] => currency format pattern
* [DECIMAL_SEPARATOR] => currency decimal separator
* [GROUPING_SEPARATOR] => currency grouping separator
* [PLURALS_OFFSET+p] => plural name where p=standardPlural.ordinal()
* Single-item cache for getName(), getSymbol(), and getFormatInfo().
* Holds data for only one currency. If another currency is requested, the old cache item is overwritten.
*/
private static class RawCurrencyData {
static final int DISPLAY_NAME = 0;
static final int SYMBOL = 1;
static final int NARROW_SYMBOL = 2;
static final int FORMAT_PATTERN = 3;
static final int DECIMAL_SEPARATOR = 4;
static final int GROUPING_SEPARATOR = 5;
static final int PLURALS_OFFSET = 6;
static final int CURRENCY_STRINGS_LENGTH = 6 + StandardPlural.COUNT;
private volatile FormattingData formattingDataCache = null;
Map<String, String[]> isoCodeToCurrencyStrings = new HashMap<String, String[]>();
/**
* Single-item cache for getNarrowSymbol().
* Holds data for only one currency. If another currency is requested, the old cache item is overwritten.
*/
private volatile NarrowSymbol narrowSymbolCache = null;
// The following maps are redundant data with the above map, but the API for CurrencyDisplayNames
// restricts us to using these data structures.
Map<String, String> symbolToIsoCode = new HashMap<String, String>();
Map<String, String> nameToIsoCode = new HashMap<String, String>();
/**
* Single-item cache for getPluralName().
*
* <p>
* array[0] is the ISO code.<br>
* array[1+p] is the plural name where p=standardPlural.ordinal().
*
* <p>
* Holds data for only one currency. If another currency is requested, the old cache item is overwritten.
*/
private volatile String[] pluralsDataCache = null;
// Other currency-related data
CurrencySpacingInfo spacingInfo = new CurrencySpacingInfo();
Map<String, String> currencyUnitPatterns = new HashMap<String, String>();
/**
* Cache for symbolMap() and nameMap().
*/
private volatile SoftReference<ParsingData> parsingDataCache = new SoftReference<ParsingData>(null);
/**
* Gets an entry out of isoCodeToCurrencyStrings or creates it if it does not exist yet.
*/
String[] getOrCreateCurrencyStrings(String isoCode) {
String[] currencyStrings = isoCodeToCurrencyStrings.get(isoCode);
if (currencyStrings == null) {
currencyStrings = new String[CURRENCY_STRINGS_LENGTH];
isoCodeToCurrencyStrings.put(isoCode, currencyStrings);
}
return currencyStrings;
}
/**
* Cache for getUnitPatterns().
*/
private volatile Map<String, String> unitPatternsCache = null;
/**
* Called after all data is loaded to convert the externally visible Maps to Unmodifiable.
*/
void freezeMaps() {
symbolToIsoCode = Collections.unmodifiableMap(symbolToIsoCode);
nameToIsoCode = Collections.unmodifiableMap(nameToIsoCode);
currencyUnitPatterns = Collections.unmodifiableMap(currencyUnitPatterns);
}
/**
* Cache for getSpacingInfo().
*/
private volatile CurrencySpacingInfo spacingInfoCache = null;
static class FormattingData {
final String isoCode;
String displayName = null;
String symbol = null;
CurrencyFormatInfo formatInfo = null;
FormattingData(String isoCode) { this.isoCode = isoCode; }
}
public ICUCurrencyDisplayInfo(ICUResourceBundle rb, boolean fallback) {
static class NarrowSymbol {
final String isoCode;
String narrowSymbol = null;
NarrowSymbol(String isoCode) { this.isoCode = isoCode; }
}
static class ParsingData {
Map<String, String> symbolToIsoCode = new HashMap<String, String>();
Map<String, String> nameToIsoCode = new HashMap<String, String>();
}
////////////////////////
/// START PUBLIC API ///
////////////////////////
public ICUCurrencyDisplayInfo(ULocale locale, ICUResourceBundle rb, boolean fallback) {
this.locale = locale;
this.fallback = fallback;
this.rb = rb;
rawDataCache = new SoftReference<RawCurrencyData>(null);
}
@Override
@ -122,50 +143,57 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
@Override
public String getName(String isoCode) {
return getName(isoCode, RawCurrencyData.DISPLAY_NAME);
FormattingData formattingData = fetchFormattingData(isoCode);
// Fall back to ISO Code
if (formattingData.displayName == null && fallback) {
return isoCode;
}
return formattingData.displayName;
}
@Override
public String getSymbol(String isoCode) {
return getName(isoCode, RawCurrencyData.SYMBOL);
FormattingData formattingData = fetchFormattingData(isoCode);
// Fall back to ISO Code
if (formattingData.symbol == null && fallback) {
return isoCode;
}
return formattingData.symbol;
}
@Override
public String getNarrowSymbol(String isoCode) {
// TODO: Should this fall back to the regular symbol instead of the ISO code?
return getName(isoCode, RawCurrencyData.NARROW_SYMBOL);
}
NarrowSymbol narrowSymbol = fetchNarrowSymbol(isoCode);
private String getName(String isoCode, int index) {
String[] currencyStrings = getRawCurrencyData().isoCodeToCurrencyStrings.get(isoCode);
String result = null;
if (currencyStrings != null) {
result = currencyStrings[index];
// Fall back to ISO Code
// TODO: Should this fall back to the regular symbol instead of the ISO code?
if (narrowSymbol.narrowSymbol == null && fallback) {
return isoCode;
}
// If fallback is true, don't return null; return the ISO code
if (result == null && fallback) {
result = isoCode;
}
return result;
return narrowSymbol.narrowSymbol;
}
@Override
public String getPluralName(String isoCode, String pluralKey ) {
StandardPlural plural = StandardPlural.orNullFromString(pluralKey);
String[] currencyStrings = getRawCurrencyData().isoCodeToCurrencyStrings.get(isoCode);
String result = null;
if (currencyStrings != null && plural != null) {
result = currencyStrings[RawCurrencyData.PLURALS_OFFSET + plural.ordinal()];
}
String[] pluralsData = fetchPluralsData(isoCode);
// See http://unicode.org/reports/tr35/#Currencies, especially the fallback rule.
if (result == null && currencyStrings != null && fallback) {
String result = null;
if (plural != null) {
result = pluralsData[1 + plural.ordinal()];
}
if (result == null && fallback) {
// First fall back to the "other" plural variant
// Note: If plural is already "other", this fallback is benign
result = currencyStrings[RawCurrencyData.PLURALS_OFFSET + StandardPlural.OTHER.ordinal()];
result = pluralsData[1 + StandardPlural.OTHER.ordinal()];
}
if (result == null && currencyStrings != null && fallback) {
if (result == null && fallback) {
// If that fails, fall back to the display name
result = currencyStrings[0];
FormattingData formattingData = fetchFormattingData(isoCode);
result = formattingData.displayName;
}
if (result == null && fallback) {
// If all else fails, return the ISO code
@ -176,69 +204,149 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
@Override
public Map<String, String> symbolMap() {
return getRawCurrencyData().symbolToIsoCode;
ParsingData parsingData = fetchParsingData();
return parsingData.symbolToIsoCode;
}
@Override
public Map<String, String> nameMap() {
return getRawCurrencyData().nameToIsoCode;
ParsingData parsingData = fetchParsingData();
return parsingData.nameToIsoCode;
}
@Override
public Map<String, String> getUnitPatterns() {
// Default result is the empty map. Callers who require a pattern will have to
// supply a default.
return getRawCurrencyData().currencyUnitPatterns;
Map<String,String> unitPatterns = fetchUnitPatterns();
return unitPatterns;
}
@Override
public CurrencyFormatInfo getFormatInfo(String isoCode) {
String[] currencyStrings = getRawCurrencyData().isoCodeToCurrencyStrings.get(isoCode);
if (currencyStrings == null || currencyStrings[RawCurrencyData.FORMAT_PATTERN] == null) {
return null;
}
String pattern = currencyStrings[RawCurrencyData.FORMAT_PATTERN];
String decimalSeparator = currencyStrings[RawCurrencyData.DECIMAL_SEPARATOR];
String groupingSeparator = currencyStrings[RawCurrencyData.GROUPING_SEPARATOR];
return new CurrencyFormatInfo(pattern, decimalSeparator, groupingSeparator);
FormattingData formattingData = fetchFormattingData(isoCode);
return formattingData.formatInfo;
}
@Override
public CurrencySpacingInfo getSpacingInfo() {
CurrencySpacingInfo result = getRawCurrencyData().spacingInfo;
if (result != null && (!result.hasBeforeCurrency || !result.hasAfterCurrency) && fallback) {
result = CurrencySpacingInfo.DEFAULT;
CurrencySpacingInfo spacingInfo = fetchSpacingInfo();
// Fall back to DEFAULT
if ((!spacingInfo.hasBeforeCurrency || !spacingInfo.hasAfterCurrency) && fallback) {
return CurrencySpacingInfo.DEFAULT;
}
if (result == null && fallback) {
result = CurrencySpacingInfo.DEFAULT;
return spacingInfo;
}
/////////////////////////////////////////////
/// END PUBLIC API -- START DATA FRONTEND ///
/////////////////////////////////////////////
FormattingData fetchFormattingData(String isoCode) {
FormattingData result = formattingDataCache;
if (result == null || !result.isoCode.equals(isoCode)) {
result = new FormattingData(isoCode);
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.CURRENCIES);
sink.formattingData = result;
rb.getAllItemsWithFallbackNoFail("Currencies/" + isoCode, sink);
formattingDataCache = result;
}
return result;
}
/**
* If the soft cache is populated, returns the data stored there.
* Otherwise, computes the data, stores it in the cache, and returns it.
* Never returns null.
*/
private RawCurrencyData getRawCurrencyData() {
RawCurrencyData data = rawDataCache.get();
if (data == null) {
data = new RawCurrencyData();
RawCurrencyDataSink sink = new RawCurrencyDataSink(data, !fallback);
rb.getAllItemsWithFallback("", sink);
data.freezeMaps();
rawDataCache = new SoftReference<RawCurrencyData>(data);
NarrowSymbol fetchNarrowSymbol(String isoCode) {
NarrowSymbol result = narrowSymbolCache;
if (result == null || !result.isoCode.equals(isoCode)) {
result = new NarrowSymbol(isoCode);
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.CURRENCY_NARROW);
sink.narrowSymbol = result;
rb.getAllItemsWithFallbackNoFail("Currencies%narrow/" + isoCode, sink);
narrowSymbolCache = result;
}
return data;
return result;
}
private static final class RawCurrencyDataSink extends UResource.Sink {
private final RawCurrencyData data;
private final boolean noRoot;
String[] fetchPluralsData(String isoCode) {
String[] result = pluralsDataCache;
if (result == null || !result[0].equals(isoCode)) {
result = new String[1 + StandardPlural.COUNT];
result[0] = isoCode;
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.CURRENCY_PLURALS);
sink.pluralsData = result;
rb.getAllItemsWithFallbackNoFail("CurrencyPlurals/" + isoCode, sink);
pluralsDataCache = result;
}
return result;
}
RawCurrencyDataSink(RawCurrencyData data, boolean noRoot) {
this.data = data;
ParsingData fetchParsingData() {
ParsingData result = parsingDataCache.get();
if (result == null) {
result = new ParsingData();
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.TOP);
sink.parsingData = result;
rb.getAllItemsWithFallback("", sink);
parsingDataCache = new SoftReference<ParsingData>(result);
}
return result;
}
Map<String, String> fetchUnitPatterns() {
Map<String, String> result = unitPatternsCache;
if (result == null) {
result = new HashMap<String, String>();
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.CURRENCY_UNIT_PATTERNS);
sink.unitPatterns = result;
rb.getAllItemsWithFallback("CurrencyUnitPatterns", sink);
unitPatternsCache = result;
}
return result;
}
CurrencySpacingInfo fetchSpacingInfo() {
CurrencySpacingInfo result = spacingInfoCache;
if (result == null) {
result = new CurrencySpacingInfo();
CurrencySink sink = new CurrencySink(!fallback, CurrencySink.EntrypointTable.CURRENCY_SPACING);
sink.spacingInfo = result;
rb.getAllItemsWithFallback("currencySpacing", sink);
spacingInfoCache = result;
}
return result;
}
////////////////////////////////////////////
/// END DATA FRONTEND -- START DATA SINK ///
////////////////////////////////////////////
private static final class CurrencySink extends UResource.Sink {
final boolean noRoot;
final EntrypointTable entrypointTable;
// The fields to be populated on this run of the data sink will be non-null.
FormattingData formattingData = null;
String[] pluralsData = null;
ParsingData parsingData = null;
Map<String, String> unitPatterns = null;
CurrencySpacingInfo spacingInfo = null;
NarrowSymbol narrowSymbol = null;
enum EntrypointTable {
// For Parsing:
TOP,
// For Formatting:
CURRENCIES,
CURRENCY_PLURALS,
CURRENCY_NARROW,
CURRENCY_SPACING,
CURRENCY_UNIT_PATTERNS
}
CurrencySink(boolean noRoot, EntrypointTable entrypointTable) {
this.noRoot = noRoot;
this.entrypointTable = entrypointTable;
}
/**
@ -246,26 +354,43 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* found in the currency data.
*/
@Override
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
if (noRoot && noFallback) {
public void put(UResource.Key key, UResource.Value value, boolean isRoot) {
if (noRoot && isRoot) {
// Don't consume the root bundle
return;
}
switch (entrypointTable) {
case TOP:
consumeTopTable(key, value);
break;
case CURRENCIES:
consumeCurrenciesEntry(key, value);
break;
case CURRENCY_PLURALS:
consumeCurrencyPluralsEntry(key, value);
break;
case CURRENCY_NARROW:
consumeCurrenciesNarrowEntry(key, value);
break;
case CURRENCY_SPACING:
consumeCurrencySpacingTable(key, value);
break;
case CURRENCY_UNIT_PATTERNS:
consumeCurrencyUnitPatternsTable(key, value);
break;
}
}
private void consumeTopTable(UResource.Key key, UResource.Value value) {
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
if (key.contentEquals("Currencies")) {
consumeCurrencies(key, value, noFallback);
} else if (key.contentEquals("Currencies%narrow")) {
consumeCurrenciesNarrow(key, value, noFallback);
consumeCurrenciesTable(key, value);
} else if (key.contentEquals("Currencies%variant")) {
consumeCurrenciesVariant(key, value, noFallback);
consumeCurrenciesVariantTable(key, value);
} else if (key.contentEquals("CurrencyPlurals")) {
consumeCurrencyPlurals(key, value, noFallback);
} else if (key.contentEquals("currencySpacing")) {
consumeCurrencySpacing(key, value, noFallback);
} else if (key.contentEquals("CurrencyUnitPatterns")) {
consumeCurrencyUnitPatterns(key, value, noFallback);
consumeCurrencyPluralsTable(key, value);
}
}
}
@ -278,62 +403,67 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* "US Dollar", => display name
* }
* ...
* ESP{
* "", => symbol
* "pesseta espanyola", => display name
* {
* "¤ #,##0.00", => currency-specific pattern
* ",", => currency-specific grouping separator
* ".", => currency-specific decimal separator
* ESP{
* "", => symbol
* "pesseta espanyola", => display name
* {
* "¤ #,##0.00", => currency-specific pattern
* ",", => currency-specific grouping separator
* ".", => currency-specific decimal separator
* }
* }
* }
* ...
* }
*/
private void consumeCurrencies(UResource.Key key, UResource.Value value, boolean noFallback) {
void consumeCurrenciesTable(UResource.Key key, UResource.Value value) {
// The full Currencies table is consumed for parsing only.
assert parsingData != null;
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
String isoCode = key.toString();
String[] currencyStrings = data.getOrCreateCurrencyStrings(isoCode);
if (value.getType() != UResourceBundle.ARRAY) {
throw new ICUException("Unexpected data type in Currencies table for " + isoCode);
}
UResource.Array array = value.getArray();
// First element is the symbol.
parsingData.symbolToIsoCode.put(isoCode, isoCode); // Add the ISO code itself as a symbol
array.getValue(0, value);
String symbol = value.getString();
if (currencyStrings[RawCurrencyData.SYMBOL] == null) {
currencyStrings[RawCurrencyData.SYMBOL] = symbol;
}
// Second element is the display name.
parsingData.symbolToIsoCode.put(value.getString(), isoCode);
array.getValue(1, value);
String name = value.getString();
if (currencyStrings[RawCurrencyData.DISPLAY_NAME] == null) {
currencyStrings[RawCurrencyData.DISPLAY_NAME] = name;
}
parsingData.nameToIsoCode.put(value.getString(), isoCode);
}
}
// If present, the third element is the currency format info.
// TODO: Write unit test to ensure that this data is being used by number formatting.
if (array.getSize() > 2 && currencyStrings[RawCurrencyData.FORMAT_PATTERN] == null) {
array.getValue(2, value);
UResource.Array formatArray = value.getArray();
formatArray.getValue(0, value);
currencyStrings[RawCurrencyData.FORMAT_PATTERN] = value.getString();
assert currencyStrings[RawCurrencyData.DECIMAL_SEPARATOR] == null;
formatArray.getValue(1, value);
currencyStrings[RawCurrencyData.DECIMAL_SEPARATOR] = value.getString();
assert currencyStrings[RawCurrencyData.GROUPING_SEPARATOR] == null;
formatArray.getValue(2, value);
currencyStrings[RawCurrencyData.GROUPING_SEPARATOR] = value.getString();
}
void consumeCurrenciesEntry(UResource.Key key, UResource.Value value) {
assert formattingData != null;
String isoCode = key.toString();
if (value.getType() != UResourceBundle.ARRAY) {
throw new ICUException("Unexpected data type in Currencies table for " + isoCode);
}
UResource.Array array = value.getArray();
// Add the name and symbols to the other two maps (used for parsing).
data.nameToIsoCode.put(name, isoCode);
data.symbolToIsoCode.put(isoCode, isoCode); // Add the ISO code itself as a symbol
data.symbolToIsoCode.put(symbol, isoCode);
if (formattingData.symbol == null) {
array.getValue(0, value);
formattingData.symbol = value.getString();
}
if (formattingData.displayName == null) {
array.getValue(1, value);
formattingData.displayName = value.getString();
}
// If present, the third element is the currency format info.
// TODO: Write unit test to ensure that this data is being used by number formatting.
if (array.getSize() > 2 && formattingData.formatInfo == null) {
array.getValue(2, value);
UResource.Array formatArray = value.getArray();
formatArray.getValue(0, value);
String formatPattern = value.getString();
formatArray.getValue(1, value);
String decimalSeparator = value.getString();
formatArray.getValue(2, value);
String groupingSeparator = value.getString();
formattingData.formatInfo = new CurrencyFormatInfo(
isoCode, formatPattern, decimalSeparator, groupingSeparator);
}
}
@ -344,16 +474,11 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* ...
* }
*/
private void consumeCurrenciesNarrow(UResource.Key key, UResource.Value value, boolean noFallback) {
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
String isoCode = key.toString();
String[] currencyStrings = data.getOrCreateCurrencyStrings(isoCode);
if (currencyStrings[RawCurrencyData.NARROW_SYMBOL] == null) {
currencyStrings[RawCurrencyData.NARROW_SYMBOL] = value.getString();
}
// Note: This data is used for formatting but not parsing.
void consumeCurrenciesNarrowEntry(UResource.Key key, UResource.Value value) {
assert narrowSymbol != null;
// No extra structure to traverse.
if (narrowSymbol.narrowSymbol == null) {
narrowSymbol.narrowSymbol = value.getString();
}
}
@ -362,13 +487,13 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* TRY{"TL"}
* }
*/
private void consumeCurrenciesVariant(UResource.Key key, UResource.Value value, boolean noFallback) {
void consumeCurrenciesVariantTable(UResource.Key key, UResource.Value value) {
// Note: This data is used for parsing but not formatting.
assert parsingData != null;
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
String isoCode = key.toString();
// Note: This data is used for parsing but not formatting.
data.symbolToIsoCode.put(value.getString(), isoCode);
parsingData.symbolToIsoCode.put(value.getString(), isoCode);
}
}
@ -381,24 +506,35 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* ...
* }
*/
private void consumeCurrencyPlurals(UResource.Key key, UResource.Value value, boolean noFallback) {
void consumeCurrencyPluralsTable(UResource.Key key, UResource.Value value) {
// The full CurrencyPlurals table is consumed for parsing only.
assert parsingData != null;
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
String isoCode = key.toString();
String[] currencyStrings = data.getOrCreateCurrencyStrings(isoCode);
UResource.Table pluralsTable = value.getTable();
for (int j=0; pluralsTable.getKeyAndValue(j, key, value); j++) {
StandardPlural plural = StandardPlural.orNullFromString(key.toString());
if (plural == null) {
throw new ICUException("Could not make StandardPlural from keyword " + key);
}
String valueString = value.getString();
if (currencyStrings[RawCurrencyData.PLURALS_OFFSET + plural.ordinal()] == null) {
currencyStrings[RawCurrencyData.PLURALS_OFFSET + plural.ordinal()] = valueString;
}
// Add the name to the name-to-currency map (used for parsing)
data.nameToIsoCode.put(valueString, isoCode);
parsingData.nameToIsoCode.put(value.getString(), isoCode);
}
}
}
void consumeCurrencyPluralsEntry(UResource.Key key, UResource.Value value) {
assert pluralsData != null;
UResource.Table pluralsTable = value.getTable();
for (int j=0; pluralsTable.getKeyAndValue(j, key, value); j++) {
StandardPlural plural = StandardPlural.orNullFromString(key.toString());
if (plural == null) {
throw new ICUException("Could not make StandardPlural from keyword " + key);
}
if (pluralsData[1 + plural.ordinal()] == null) {
pluralsData[1 + plural.ordinal()] = value.getString();
}
}
}
@ -417,16 +553,17 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* }
* }
*/
private void consumeCurrencySpacing(UResource.Key key, UResource.Value value, boolean noFallback) {
void consumeCurrencySpacingTable(UResource.Key key, UResource.Value value) {
assert spacingInfo != null;
UResource.Table spacingTypesTable = value.getTable();
for (int i = 0; spacingTypesTable.getKeyAndValue(i, key, value); ++i) {
CurrencySpacingInfo.SpacingType type;
if (key.contentEquals("beforeCurrency")) {
type = CurrencySpacingInfo.SpacingType.BEFORE;
data.spacingInfo.hasBeforeCurrency = true;
spacingInfo.hasBeforeCurrency = true;
} else if (key.contentEquals("afterCurrency")) {
type = CurrencySpacingInfo.SpacingType.AFTER;
data.spacingInfo.hasAfterCurrency = true;
spacingInfo.hasAfterCurrency = true;
} else {
continue;
}
@ -444,7 +581,7 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
continue;
}
data.spacingInfo.setSymbolIfNull(type, pattern, value.getString());
spacingInfo.setSymbolIfNull(type, pattern, value.getString());
}
}
}
@ -455,12 +592,13 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
* ...
* }
*/
private void consumeCurrencyUnitPatterns(UResource.Key key, UResource.Value value, boolean noFallback) {
void consumeCurrencyUnitPatternsTable(UResource.Key key, UResource.Value value) {
assert unitPatterns != null;
UResource.Table table = value.getTable();
for (int i = 0; table.getKeyAndValue(i, key, value); i++) {
String pluralKeyword = key.toString();
if (data.currencyUnitPatterns.get(pluralKeyword) == null) {
data.currencyUnitPatterns.put(pluralKeyword, value.getString());
if (unitPatterns.get(pluralKeyword) == null) {
unitPatterns.put(pluralKeyword, value.getString());
}
}
}