ICU-10640 Change MeasureFormat to use QuantityFormatter.

X-SVN-Rev: 35030
This commit is contained in:
Travis Keep 2014-01-30 01:16:05 +00:00
parent 55e2a4a900
commit b50eda3341
2 changed files with 57 additions and 62 deletions

View File

@ -25,13 +25,13 @@ import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.MissingResourceException;
import java.util.Set;
import com.ibm.icu.impl.DontCareFieldPosition;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.SimplePatternFormatter;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.Measure;
@ -117,14 +117,14 @@ public class MeasureFormat extends UFormat {
private final transient PluralRules rules;
// Measure unit -> format width -> plural form -> pattern ("{0} meters")
private final transient Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat;
private final transient Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat;
private final transient NumericFormatters numericFormatters;
private final transient ImmutableNumberFormat currencyFormat;
private static final SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>> localeToUnitToStyleToCountToFormat
= new SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>>();
private static final SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>> localeToUnitToStyleToCountToFormat
= new SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>>();
private static final SimpleCache<ULocale, NumericFormatters> localeToNumericDurationFormatters
= new SimpleCache<ULocale,NumericFormatters>();
@ -234,7 +234,7 @@ public class MeasureFormat extends UFormat {
*/
public static MeasureFormat getInstance(ULocale locale, FormatWidth formatWidth, NumberFormat format) {
PluralRules rules = PluralRules.forLocale(locale);
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat;
Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat;
NumericFormatters formatters = null;
unitToStyleToCountToFormat = localeToUnitToStyleToCountToFormat.get(locale);
if (unitToStyleToCountToFormat == null) {
@ -482,7 +482,7 @@ public class MeasureFormat extends UFormat {
FormatWidth formatWidth,
ImmutableNumberFormat format,
PluralRules rules,
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat,
Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat,
NumericFormatters formatters,
ImmutableNumberFormat currencyFormat) {
setLocale(locale, locale);
@ -536,56 +536,38 @@ public class MeasureFormat extends UFormat {
/**
* Returns formatting data for all MeasureUnits except for currency ones.
*/
private static Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> loadLocaleData(
private static Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> loadLocaleData(
ULocale locale, PluralRules rules) {
Set<String> keywords = rules.getKeywords();
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat
= new HashMap<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>();
QuantityFormatter.Builder builder = new QuantityFormatter.Builder();
Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat
= new HashMap<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>();
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
for (MeasureUnit unit : MeasureUnit.getAvailable()) {
// Currency data cannot be found here. Skip.
if (unit instanceof Currency) {
continue;
}
EnumMap<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
EnumMap<FormatWidth, QuantityFormatter> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
if (styleToCountToFormat == null) {
unitToStyleToCountToFormat.put(unit, styleToCountToFormat = new EnumMap<FormatWidth, Map<String, PatternData>>(FormatWidth.class));
unitToStyleToCountToFormat.put(unit, styleToCountToFormat = new EnumMap<FormatWidth, QuantityFormatter>(FormatWidth.class));
}
for (FormatWidth styleItem : FormatWidth.values()) {
try {
ICUResourceBundle unitTypeRes = resource.getWithFallback(styleItem.resourceKey);
ICUResourceBundle unitsRes = unitTypeRes.getWithFallback(unit.getType());
ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(unit.getSubtype());
Map<String, PatternData> countToFormat = styleToCountToFormat.get(styleItem);
if (countToFormat == null) {
styleToCountToFormat.put(styleItem, countToFormat = new HashMap<String, PatternData>());
}
// TODO(rocketman): Seems like we should be iterating over the bundles in
// oneUnitRes instead of all the plural key words since most languages have
// just 1 or 2 forms.
for (String keyword : keywords) {
builder.reset();
int len = oneUnitRes.getSize();
for (int i = 0; i < len; i++) {
UResourceBundle countBundle;
try {
countBundle = oneUnitRes.get(keyword);
countBundle = oneUnitRes.get(i);
} catch (MissingResourceException e) {
continue;
}
String pattern = countBundle.getString();
// System.out.println(styleItem.resourceKey + "/"
// + unit.getType() + "/"
// + unit.getCode() + "/"
// + keyword + "=" + pattern);
PatternData format = new PatternData(pattern);
countToFormat.put(keyword, format);
// System.out.println(styleToCountToFormat);
}
// fill in 'other' for any missing values
PatternData other = countToFormat.get("other");
for (String keyword : keywords) {
if (!countToFormat.containsKey(keyword)) {
countToFormat.put(keyword, other);
}
builder.add(countBundle.getKey(), countBundle.getString());
}
styleToCountToFormat.put(styleItem, builder.build());
} catch (MissingResourceException e) {
continue;
}
@ -593,7 +575,7 @@ public class MeasureFormat extends UFormat {
// now fill in the holes
fillin:
if (styleToCountToFormat.size() != FormatWidth.values().length) {
Map<String, PatternData> fallback = styleToCountToFormat.get(FormatWidth.SHORT);
QuantityFormatter fallback = styleToCountToFormat.get(FormatWidth.SHORT);
if (fallback == null) {
fallback = styleToCountToFormat.get(FormatWidth.WIDE);
}
@ -601,12 +583,9 @@ public class MeasureFormat extends UFormat {
break fillin; // TODO use root
}
for (FormatWidth styleItem : FormatWidth.values()) {
Map<String, PatternData> countToFormat = styleToCountToFormat.get(styleItem);
QuantityFormatter countToFormat = styleToCountToFormat.get(styleItem);
if (countToFormat == null) {
styleToCountToFormat.put(styleItem, countToFormat = new HashMap<String, PatternData>());
for (Entry<String, PatternData> entry : fallback.entrySet()) {
countToFormat.put(entry.getKey(), entry.getValue());
}
styleToCountToFormat.put(styleItem, fallback);
}
}
}
@ -636,18 +615,18 @@ public class MeasureFormat extends UFormat {
StringBuffer formattedNumber = numberFormat.format(n, new StringBuffer(), fpos);
String keyword = rules.select(new PluralRules.FixedDecimal(n.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
Map<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
Map<String, PatternData> countToFormat = styleToCountToFormat.get(formatWidth);
PatternData messagePatternData = countToFormat.get(keyword);
appendTo.append(messagePatternData.prefix);
if (messagePatternData.suffix != null) { // there is a number (may not happen with, say, Arabic dual)
Map<FormatWidth, QuantityFormatter> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
QuantityFormatter countToFormat = styleToCountToFormat.get(formatWidth);
SimplePatternFormatter formatter = countToFormat.getByVariant(keyword);
SimplePatternFormatter.Formatted result = formatter.formatValues(new Object[] {formattedNumber});
appendTo.append(result.toString());
int offset = result.getOffset(0);
if (offset != -1) { // there is a number (may not happen with, say, Arabic dual)
// Fix field position
if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
fieldPosition.setBeginIndex(fpos.getBeginIndex() + messagePatternData.prefix.length());
fieldPosition.setEndIndex(fpos.getEndIndex() + messagePatternData.prefix.length());
fieldPosition.setBeginIndex(fpos.getBeginIndex() + offset);
fieldPosition.setEndIndex(fpos.getEndIndex() + offset);
}
appendTo.append(formattedNumber);
appendTo.append(messagePatternData.suffix);
}
return appendTo;
}

View File

@ -82,12 +82,10 @@ class QuantityFormatter {
* Builds the new QuantityFormatter and resets this Builder to its initial state.
* @return the new QuantityFormatter object.
* @throws IllegalStateException if no template is specified for the "other" variant.
* When throwing this exception, build() still resets this Builder to its initial
* state.
* When throwing this exception, build leaves this builder in its current state.
*/
public QuantityFormatter build() {
if (templates == null || templates[0] == null) {
templates = null;
throw new IllegalStateException("At least other variant must be set.");
}
QuantityFormatter result = new QuantityFormatter(templates);
@ -95,6 +93,15 @@ class QuantityFormatter {
return result;
}
/**
* Resets this builder to its intitial state.
*/
public Builder reset() {
templates = null;
return this;
}
}
private final SimplePatternFormatter[] templates;
@ -113,18 +120,27 @@ class QuantityFormatter {
*/
public String format(double quantity, NumberFormat numberFormat, PluralRules pluralRules) {
String formatStr = numberFormat.format(quantity);
String variant;
if (numberFormat instanceof DecimalFormat) {
variant = pluralRules.select(((DecimalFormat) numberFormat).getFixedDecimal(quantity));
} else {
variant = pluralRules.select(quantity);
}
String variant = computeVariant(quantity, numberFormat, pluralRules);
return getByVariant(variant).format(formatStr);
}
private SimplePatternFormatter getByVariant(String variant) {
/**
* Gets the SimplePatternFormatter for a particular variant.
* @param variant "zero", "one", "two", "few", "many", "other"
* @return the SimplePatternFormatter
*/
public SimplePatternFormatter getByVariant(String variant) {
Integer idxObj = INDEX_MAP.get(variant);
SimplePatternFormatter template = templates[idxObj == null ? 0 : idxObj.intValue()];
return template == null ? templates[0] : template;
}
private String computeVariant(double quantity, NumberFormat numberFormat, PluralRules pluralRules) {
if (numberFormat instanceof DecimalFormat) {
return pluralRules.select(((DecimalFormat) numberFormat).getFixedDecimal(quantity));
}
return pluralRules.select(quantity);
}
}