blob: 8a1e7f54a70f419ac18ea48ec543830ad4c48d47 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
/**
* A cache containing data by locale for {@link CompactDecimalFormat}
*
* @author Travis Keep
*/
class CompactDecimalDataCache {
private static final int MAX_DIGITS = 15;
private final ICUCache<ULocale, Data> cache = new SimpleCache<ULocale, Data>();
/**
* Data contains the compact decimal data for a particular locale. Data consists
* of three arrays. The index of each array corresponds to log10 of the number
* being formatted, so when formatting 12,345, the 4th index of the arrays should
* be used. Divisors contain the number to divide by before doing formatting.
* In the case of english, <code>divisors[4]</code> is 1000. So to format
* 12,345, divide by 1000 to get 12. prefix and suffix contain the prefix and
* suffix to use, for english, <code>suffix[4]</code> is "K" So ultimately,
* 12,345 is formatted as 12K.
*
* Each array in data is 15 in length, and every index is filled.
*
* @author Travis Keep
* *
*/
static class Data {
long[] divisors;
String[] prefixes;
String[] suffixes;
Data(long[] divisors, String[] prefixes, String[] suffixes) {
this.divisors = divisors;
this.prefixes = prefixes;
this.suffixes = suffixes;
}
}
/**
* Fetch data for a particular locale.
*/
Data get(ULocale locale) {
Data result = cache.get(locale);
if (result == null) {
result = load(locale);
cache.put(locale, result);
}
return result;
}
private static Data load(ULocale ulocale) {
NumberingSystem ns = NumberingSystem.getInstance(ulocale);
ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, ulocale);
r = r.getWithFallback("NumberElements/" + ns.getName() + "/patternsShort/decimalFormat");
int size = r.getSize();
Data result = new Data(
new long[MAX_DIGITS], new String[MAX_DIGITS], new String[MAX_DIGITS]);
for (int i = 0; i < size; i++) {
populateData((ICUResourceBundle) r.get(i), result);
}
fillInMissingAndFixRedundant(result);
return result;
}
/**
* Populates Data object with data for a particular divisor from resource bundle.
*/
private static void populateData(ICUResourceBundle divisorData, Data result) {
long divisor = Long.parseLong(divisorData.getKey());
int thisIndex = (int) Math.log10(divisor);
// Silently ignore divisors that are too big.
if (thisIndex >= MAX_DIGITS) {
return;
}
ICUResourceBundle other = (ICUResourceBundle) divisorData.get("other");
populatePrefixSuffix(other.getString(), thisIndex, result);
result.divisors[thisIndex] = divisor;
}
/**
* Extracts the prefix and suffix from the template and places them in the
* Data object.
* @param template the number template, e.g 000K
* @param idx the index to store the extracted prefix and suffix
* @param result Data object modified in-place here.
*/
private static void populatePrefixSuffix(String template, int idx, Data result) {
int firstIdx = template.indexOf("0");
int lastIdx = template.lastIndexOf("0");
result.prefixes[idx] = template.substring(0, firstIdx);
result.suffixes[idx] = template.substring(lastIdx + 1);
}
/**
* After reading information from resource bundle into a Data object, there
* is no guarantee that every index of the arrays will be filled. Moreover,
* the resource bundle may contain redundant information. After reading
* the resource for english, we may end up with:
* <pre>
* index divisor prefix suffix
* 0 <none> <none> <none>
* 1 <none> <none> <none>
* 2 <none> <none> <none>
* 3 1000 K
* 4 10000 K
* 5 100000 K
* 6 1000000 M
* ...
* </pre>
* The 10000 and 100000 are redundant. In fact, this data will cause CompactDecimalFormatter
* to format 12345 as 1.2K instead of 12K. Moreover, no data was populated for
* indexes 0, 1, or 2. What we want is something like this:
* <pre>
* index divisor prefix suffix
* 0 1
* 1 1
* 2 1
* 3 1000 K
* 4 1000 K
* 5 1000 K
* 6 1000000 M
* ...
* </pre>
* This function walks through the arrays filling in missing data and replacing
* redundant data. If prefix is missing for an index, we fill in that index
* with the values from the previous index. If prefix and suffix for an index
* are the same as the previous index, we consider that redundant data and we
* replace the divisor with the one from the previous index.
*
* @param result this instance is fixed in-place.
*/
private static void fillInMissingAndFixRedundant(Data result) {
// Initially we assume that previous divisor is 1 with no prefix or suffix.
long lastDivisor = 1L;
String lastPrefix = "";
String lastSuffix = "";
for (int i = 0; i < result.divisors.length; i++) {
if (result.prefixes[i] == null) {
result.divisors[i] = lastDivisor;
result.prefixes[i] = lastPrefix;
result.suffixes[i] = lastSuffix;
} else if (result.prefixes[i].equals(lastPrefix) && result.suffixes[i].equals(lastSuffix)) {
result.divisors[i] = lastDivisor;
} else {
lastDivisor = result.divisors[i];
lastPrefix = result.prefixes[i];
lastSuffix = result.suffixes[i];
}
}
}
}