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;
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>();
*/
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) {
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);
/**
* 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;
}
// 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);
}
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);
}
}
}
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;
}
* 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);
return result;
}
+ /**
+ * Resets this builder to its intitial state.
+ */
+ public Builder reset() {
+ templates = null;
+ return this;
+
+ }
+
}
private final SimplePatternFormatter[] templates;
*/
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);
+ }
+
+
}