ICU-12030 MeasureFormat handle by-plural-form sideways-aliasing, by not bundling plural forms into a QuantityFormatter, reintegrate from ^/icu4j/branches/markus/sideways

X-SVN-Rev: 38122
This commit is contained in:
Markus Scherer 2015-12-11 21:02:02 +00:00
parent ad6035c5c2
commit fa027da0f7
6 changed files with 256 additions and 222 deletions

View file

@ -1,7 +1,7 @@
/*
*******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
* Copyright (C) 2014-2015, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.impl;
@ -55,6 +55,17 @@ public class SimplePatternFormatter {
* @return the new SimplePatternFormatter object.
*/
public static SimplePatternFormatter compile(String pattern) {
return compileMinMaxPlaceholders(pattern, 0, Integer.MAX_VALUE);
}
/**
* Compiles a string.
* @param pattern The string.
* @param min The pattern must have at least this many placeholders.
* @param max The pattern must have at most this many placeholders.
* @return the new SimplePatternFormatter object.
*/
public static SimplePatternFormatter compileMinMaxPlaceholders(String pattern, int min, int max) {
PlaceholdersBuilder placeholdersBuilder = new PlaceholdersBuilder();
PlaceholderIdBuilder idBuilder = new PlaceholderIdBuilder();
StringBuilder newPattern = new StringBuilder();
@ -113,8 +124,15 @@ public class SimplePatternFormatter {
default:
throw new IllegalStateException();
}
if (placeholdersBuilder.getPlaceholderCount() < min) {
throw new IllegalArgumentException(
"Fewer than minimum " + min + " placeholders in pattern \"" + pattern + "\"");
}
if (placeholdersBuilder.getPlaceholderCount() > max) {
throw new IllegalArgumentException(
"More than maximum " + max + " placeholders in pattern \"" + pattern + "\"");
}
return new SimplePatternFormatter(newPattern.toString(), placeholdersBuilder);
}
/**

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2012-2014, Google, International Business Machines Corporation and
* Copyright (C) 2012-2015, Google, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
@ -9,10 +9,8 @@ package com.ibm.icu.text;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUResourceBundle;
@ -109,14 +107,13 @@ final public class ListFormatter {
@Deprecated
public ListFormatter(String two, String start, String middle, String end) {
this(
SimplePatternFormatter.compile(two),
SimplePatternFormatter.compile(start),
SimplePatternFormatter.compile(middle),
SimplePatternFormatter.compile(end),
compilePattern(two),
compilePattern(start),
compilePattern(middle),
compilePattern(end),
null);
}
private ListFormatter(SimplePatternFormatter two, SimplePatternFormatter start, SimplePatternFormatter middle, SimplePatternFormatter end, ULocale locale) {
this.two = two;
this.start = start;
@ -125,6 +122,10 @@ final public class ListFormatter {
this.locale = locale;
}
private static SimplePatternFormatter compilePattern(String pattern) {
return SimplePatternFormatter.compileMinMaxPlaceholders(pattern, 2, 2);
}
/**
* Create a list formatter that is appropriate for a locale.
*
@ -301,16 +302,6 @@ final public class ListFormatter {
}
}
/** JUST FOR DEVELOPMENT */
// For use with the hard-coded data
// TODO Replace by use of RB
// Verify in building that all of the patterns contain {0}, {1}.
static Map<ULocale, ListFormatter> localeToData = new HashMap<ULocale, ListFormatter>();
static void add(String locale, String...data) {
localeToData.put(new ULocale(locale), new ListFormatter(data[0], data[1], data[2], data[3]));
}
private static class Cache {
private final ICUCache<String, ListFormatter> cache =
new SimpleCache<String, ListFormatter>();
@ -330,10 +321,10 @@ final public class ListFormatter {
getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, ulocale);
return new ListFormatter(
SimplePatternFormatter.compile(r.getWithFallback("listPattern/" + style + "/2").getString()),
SimplePatternFormatter.compile(r.getWithFallback("listPattern/" + style + "/start").getString()),
SimplePatternFormatter.compile(r.getWithFallback("listPattern/" + style + "/middle").getString()),
SimplePatternFormatter.compile(r.getWithFallback("listPattern/" + style + "/end").getString()),
compilePattern(r.getWithFallback("listPattern/" + style + "/2").getString()),
compilePattern(r.getWithFallback("listPattern/" + style + "/start").getString()),
compilePattern(r.getWithFallback("listPattern/" + style + "/middle").getString()),
compilePattern(r.getWithFallback("listPattern/" + style + "/end").getString()),
ulocale);
}
}

View file

@ -448,8 +448,8 @@ public class MeasureFormat extends UFormat {
// FieldPosition pos2 = new FieldPosition(0);
// currencyFormat.format(currencyHigh, buffer2, pos2);
} else {
QuantityFormatter countToFormat = getQuantityFormatter(lowValue.getUnit(), formatWidth);
SimplePatternFormatter formatter = countToFormat.getByVariant(resolvedCategory.toString());
SimplePatternFormatter formatter =
getPluralFormatter(lowValue.getUnit(), formatWidth, resolvedCategory.ordinal());
return formatter.format(formattedNumber);
}
}
@ -746,7 +746,29 @@ public class MeasureFormat extends UFormat {
* unitsShort/duration/hour contains other{"{0} hrs"}.
*/
class UnitPatternSink extends UResource.TableSink {
QuantityFormatter countToFormat;
SimplePatternFormatter[] patterns;
void setFormatterIfAbsent(int index, UResource.Value value, int minPlaceholders) {
if (patterns == null) {
EnumMap<FormatWidth, SimplePatternFormatter[]> styleToPatterns =
cacheData.unitToStyleToPatterns.get(unit);
if (styleToPatterns == null) {
styleToPatterns =
new EnumMap<FormatWidth, SimplePatternFormatter[]>(FormatWidth.class);
cacheData.unitToStyleToPatterns.put(unit, styleToPatterns);
} else {
patterns = styleToPatterns.get(width);
}
if (patterns == null) {
patterns = new SimplePatternFormatter[MeasureFormatData.PATTERN_COUNT];
styleToPatterns.put(width, patterns);
}
}
if (patterns[index] == null) {
patterns[index] = SimplePatternFormatter.
compileMinMaxPlaceholders(value.getString(), minPlaceholders, 1);
}
}
@Override
public void put(UResource.Key key, UResource.Value value) {
@ -754,27 +776,14 @@ public class MeasureFormat extends UFormat {
// Skip the unit display name for now.
} else if (key.contentEquals("per")) {
// For example, "{0}/h".
cacheData.setPerUnitFormatterIfAbsent(unit, width, value);
// TODO: Set minPlaceholders=1
// after http://unicode.org/cldr/trac/ticket/9129 is fixed.
setFormatterIfAbsent(MeasureFormatData.PER_UNIT_INDEX, value, 0);
} else {
// The key must be one of the plural form strings. For example:
// one{"{0} hr"}
// other{"{0} hrs"}
if (countToFormat == null) {
EnumMap<FormatWidth, QuantityFormatter> styleToCountToFormat =
cacheData.unitToStyleToCountToFormat.get(unit);
if (styleToCountToFormat == null) {
styleToCountToFormat =
new EnumMap<FormatWidth, QuantityFormatter>(FormatWidth.class);
cacheData.unitToStyleToCountToFormat.put(unit, styleToCountToFormat);
} else {
countToFormat = styleToCountToFormat.get(width);
}
if (countToFormat == null) {
countToFormat = new QuantityFormatter();
styleToCountToFormat.put(width, countToFormat);
}
}
countToFormat.addIfAbsent(key, value);
setFormatterIfAbsent(StandardPluralCategories.getIndex(key), value, 0);
}
}
}
@ -789,8 +798,8 @@ public class MeasureFormat extends UFormat {
public UResource.TableSink getOrCreateTableSink(UResource.Key key, int initialSize) {
// Should we ignore or reject unknown units?
unit = MeasureUnit.internalGetInstance(type, key.toString()); // never null
// Trigger a fresh lookup of the QuantityFormatter for this unit+width.
patternSink.countToFormat = null;
// Trigger a fresh lookup of the patterns for this unit+width.
patternSink.patterns = null;
return patternSink;
}
}
@ -805,7 +814,7 @@ public class MeasureFormat extends UFormat {
public void put(UResource.Key key, UResource.Value value) {
if (key.contentEquals("per")) {
cacheData.styleToPerPattern.put(width,
SimplePatternFormatter.compile(value.getString()));
SimplePatternFormatter.compileMinMaxPlaceholders(value.getString(), 2, 2));
}
}
}
@ -925,45 +934,44 @@ public class MeasureFormat extends UFormat {
return width;
}
private QuantityFormatter getQuantityFormatter(MeasureUnit unit, FormatWidth width) {
private SimplePatternFormatter getFormatterOrNull(MeasureUnit unit, FormatWidth width, int index) {
width = getRegularWidth(width);
Map<FormatWidth, QuantityFormatter> styleToCountToFormat =
cache.unitToStyleToCountToFormat.get(unit);
QuantityFormatter countToFormat = styleToCountToFormat.get(width);
if (countToFormat != null) {
return countToFormat;
Map<FormatWidth, SimplePatternFormatter[]> styleToPatterns =
cache.unitToStyleToPatterns.get(unit);
SimplePatternFormatter[] patterns = styleToPatterns.get(width);
if (patterns != null && patterns[index] != null) {
return patterns[index];
}
FormatWidth fallbackWidth = cache.widthFallback[width.ordinal()];
if (fallbackWidth != null) {
countToFormat = styleToCountToFormat.get(fallbackWidth);
if (countToFormat != null) {
return countToFormat;
}
}
throw new MissingResourceException("no formatting patterns for " + unit + " and width " + width, null, null);
}
private SimplePatternFormatter getPerUnitFormatter(MeasureUnit unit, FormatWidth width) {
width = getRegularWidth(width);
Map<FormatWidth, SimplePatternFormatter> styleToPerUnitPattern =
cache.unitToStyleToPerUnitPattern.get(unit);
if (styleToPerUnitPattern == null) {
return null;
}
SimplePatternFormatter perUnitPattern = styleToPerUnitPattern.get(width);
if (perUnitPattern != null) {
return perUnitPattern;
}
FormatWidth fallbackWidth = cache.widthFallback[width.ordinal()];
if (fallbackWidth != null) {
perUnitPattern = styleToPerUnitPattern.get(fallbackWidth);
if (perUnitPattern != null) {
return perUnitPattern;
patterns = styleToPatterns.get(fallbackWidth);
if (patterns != null && patterns[index] != null) {
return patterns[index];
}
}
return null;
}
private SimplePatternFormatter getFormatter(MeasureUnit unit, FormatWidth width, int index) {
SimplePatternFormatter pattern = getFormatterOrNull(unit, width, index);
if (pattern == null) {
throw new MissingResourceException(
"no formatting pattern for " + unit + ", width " + width + ", index " + index,
null, null);
}
return pattern;
}
private SimplePatternFormatter getPluralFormatter(MeasureUnit unit, FormatWidth width, int index) {
if (index != StandardPluralCategories.OTHER_INDEX) {
SimplePatternFormatter pattern = getFormatterOrNull(unit, width, index);
if (pattern != null) {
return pattern;
}
}
return getFormatter(unit, width, StandardPluralCategories.OTHER_INDEX);
}
private SimplePatternFormatter getPerFormatter(FormatWidth width) {
width = getRegularWidth(width);
SimplePatternFormatter perPattern = cache.styleToPerPattern.get(width);
@ -983,14 +991,16 @@ public class MeasureFormat extends UFormat {
private int withPerUnitAndAppend(
CharSequence formatted, MeasureUnit perUnit, StringBuilder appendTo) {
int[] offsets = new int[1];
SimplePatternFormatter perUnitPattern = getPerUnitFormatter(perUnit, formatWidth);
SimplePatternFormatter perUnitPattern =
getFormatterOrNull(perUnit, formatWidth, MeasureFormatData.PER_UNIT_INDEX);
if (perUnitPattern != null) {
perUnitPattern.formatAndAppend(appendTo, offsets, formatted);
return offsets[0];
}
SimplePatternFormatter perPattern = getPerFormatter(formatWidth);
QuantityFormatter countToFormat = getQuantityFormatter(perUnit, formatWidth);
String perUnitString = countToFormat.getByVariant("one").getPatternWithNoPlaceholders().trim();
SimplePatternFormatter pattern =
getPluralFormatter(perUnit, formatWidth, StandardPluralCategories.one.ordinal());
String perUnitString = pattern.getPatternWithNoPlaceholders().trim();
perPattern.formatAndAppend(appendTo, offsets, formatted, perUnitString);
return offsets[0];
}
@ -1020,8 +1030,8 @@ public class MeasureFormat extends UFormat {
StringBuffer formattedNumber = nf.format(n, new StringBuffer(), fpos);
String keyword = rules.select(new PluralRules.FixedDecimal(n.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
QuantityFormatter countToFormat = getQuantityFormatter(unit, formatWidth);
SimplePatternFormatter formatter = countToFormat.getByVariant(keyword);
int pluralForm = StandardPluralCategories.getIndexOrOtherIndex(keyword);
SimplePatternFormatter formatter = getPluralFormatter(unit, formatWidth, pluralForm);
int[] offsets = new int[1];
formatter.formatAndAppend(appendTo, offsets, formattedNumber);
if (offsets[0] != -1) { // there is a number (may not happen with, say, Arabic dual)
@ -1046,34 +1056,22 @@ public class MeasureFormat extends UFormat {
* TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
*/
private static final class MeasureFormatData {
static final int PER_UNIT_INDEX = StandardPluralCategories.COUNT;
static final int PATTERN_COUNT = PER_UNIT_INDEX + 1;
boolean hasPerFormatter(FormatWidth width) {
return styleToPerPattern.containsKey(width);
}
void setPerUnitFormatterIfAbsent(MeasureUnit unit, FormatWidth width, UResource.Value value) {
EnumMap<FormatWidth, SimplePatternFormatter> styleToPerUnitPattern =
unitToStyleToPerUnitPattern.get(unit);
if (styleToPerUnitPattern == null) {
styleToPerUnitPattern =
new EnumMap<FormatWidth, SimplePatternFormatter>(FormatWidth.class);
unitToStyleToPerUnitPattern.put(unit, styleToPerUnitPattern);
}
if (!styleToPerUnitPattern.containsKey(width)) {
styleToPerUnitPattern.put(width, SimplePatternFormatter.compile(value.getString()));
}
}
/**
* Redirection data from root-bundle, top-level sideways aliases.
* - null: initial value, just fall back to root
* - FormatWidth.WIDE/SHORT/NARROW: sideways alias for missing data
*/
final FormatWidth widthFallback[] = new FormatWidth[FormatWidth.INDEX_COUNT];
/** Measure unit -> format width -> plural form -> pattern ("{0} meters") */
final Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat =
new HashMap<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>();
final Map<MeasureUnit, EnumMap<FormatWidth, SimplePatternFormatter>> unitToStyleToPerUnitPattern =
new HashMap<MeasureUnit, EnumMap<FormatWidth, SimplePatternFormatter>>();
/** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
final Map<MeasureUnit, EnumMap<FormatWidth, SimplePatternFormatter[]>> unitToStyleToPatterns =
new HashMap<MeasureUnit, EnumMap<FormatWidth, SimplePatternFormatter[]>>();
final EnumMap<FormatWidth, SimplePatternFormatter> styleToPerPattern =
new EnumMap<FormatWidth, SimplePatternFormatter>(FormatWidth.class);;
}
@ -1463,7 +1461,7 @@ public class MeasureFormat extends UFormat {
} catch ( MissingResourceException ex ) {
resultString = rb.getStringWithFallback("NumberElements/latn/patterns/range");
}
result = SimplePatternFormatter.compile(resultString);
result = SimplePatternFormatter.compileMinMaxPlaceholders(resultString, 2, 2);
localeIdToRangeFormat.put(forLocale, result);
if (!forLocale.equals(realLocale)) {
localeIdToRangeFormat.put(realLocale, result);

View file

@ -1787,6 +1787,14 @@ public class PluralRules implements Serializable {
*/
@Deprecated
public enum StandardPluralCategories {
// TODO: An enum name is more commonly singular, e.g., StandardPluralCategory.
// TODO: Consider changing it to StandardPluralForm(s) which is shorter, and easier to say.
// We use "plural category" and "plural form" interchangeably in
// http://www.unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
// Maybe even just StandardPlural?!
// TODO: Make the constants uppercase, and change code that relies on lowercase names,
// such as by calling .valueOf(String).
// TODO: Move this into its own file, in impl package?
/**
* @internal
* @deprecated This API is ICU internal only.
@ -1823,28 +1831,119 @@ public class PluralRules implements Serializable {
*/
@Deprecated
other;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final List<StandardPluralCategories> VALUES
= Collections.unmodifiableList(Arrays.asList(values()));
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int COUNT = values().length;
static StandardPluralCategories forString(String s) {
StandardPluralCategories a;
try {
a = valueOf(s);
} catch (Exception e) {
return null;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int OTHER_INDEX = other.ordinal();
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final List<StandardPluralCategories> VALUES =
Collections.unmodifiableList(Arrays.asList(values()));
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int COUNT = VALUES.size();
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final StandardPluralCategories getCategoryOrNull(CharSequence keyword) {
switch (keyword.length()) {
case 3:
if ("one".contentEquals(keyword)) {
return one;
} else if ("two".contentEquals(keyword)) {
return two;
} else if ("few".contentEquals(keyword)) {
return few;
}
break;
case 4:
if ("many".contentEquals(keyword)) {
return many;
} else if ("zero".contentEquals(keyword)) {
return zero;
}
break;
case 5:
if ("other".contentEquals(keyword)) {
return other;
}
break;
default:
break;
}
return null;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final StandardPluralCategories getCategoryOrOther(CharSequence keyword) {
StandardPluralCategories cat = getCategoryOrNull(keyword);
return cat != null ? cat : other;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final StandardPluralCategories getCategory(CharSequence keyword) {
StandardPluralCategories cat = getCategoryOrNull(keyword);
if (cat != null) {
return cat;
} else {
throw new IllegalArgumentException(keyword.toString());
}
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int getIndexOrNegative(CharSequence keyword) {
StandardPluralCategories cat = getCategoryOrNull(keyword);
return cat != null ? cat.ordinal() : -1;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int getIndexOrOtherIndex(CharSequence keyword) {
StandardPluralCategories cat = getCategoryOrNull(keyword);
return cat != null ? cat.ordinal() : other.ordinal();
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public static final int getIndex(CharSequence keyword) {
StandardPluralCategories cat = getCategoryOrNull(keyword);
if (cat != null) {
return cat.ordinal();
} else {
throw new IllegalArgumentException(keyword.toString());
}
return a;
}
}
@ -2012,16 +2111,27 @@ public class PluralRules implements Serializable {
* Given a number information, returns the keyword of the first rule that applies to
* the number.
*
* @param sample The number information for which the rule has to be determined.
* @param number The number information for which the rule has to be determined.
* @return The keyword of the selected rule.
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public String select(FixedDecimal sample) {
return rules.select(sample);
public String select(FixedDecimal number) {
return rules.select(number);
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public String select(double number, NumberFormat numberFormat) {
if (numberFormat instanceof DecimalFormat) {
return select(((DecimalFormat) numberFormat).getFixedDecimal(number));
}
return select(number);
}
/**
* Given a number information, and keyword, return whether the keyword would match the number.

View file

@ -1,7 +1,7 @@
/*
*******************************************************************************
* Copyright (C) 2013-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
* Copyright (C) 2013-2015, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.text;
@ -9,7 +9,6 @@ package com.ibm.icu.text;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@ -21,7 +20,6 @@ import java.util.TreeSet;
import com.ibm.icu.text.PluralRules.FixedDecimal;
import com.ibm.icu.text.PluralRules.KeywordStatus;
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
import com.ibm.icu.util.Output;
/**
@ -237,17 +235,6 @@ public class PluralSamples {
return 37;
}
@SuppressWarnings("unused")
private static final Comparator<String> KEYWORD_COMPARATOR = new Comparator<String> () {
public int compare(String arg0, String arg1) {
StandardPluralCategories a = StandardPluralCategories.forString(arg0);
StandardPluralCategories b = StandardPluralCategories.forString(arg1);
return a == null
? (b == null ? arg0.compareTo(arg1) : -1)
: (b == null ? 1 : a.compareTo(b));
}
};
/**
* @internal
* @deprecated This API is ICU internal only.

View file

@ -7,7 +7,7 @@
package com.ibm.icu.text;
import com.ibm.icu.impl.SimplePatternFormatter;
import com.ibm.icu.impl.UResource;
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
/**
* QuantityFormatter represents an unknown quantity of something and formats a known quantity
@ -18,41 +18,8 @@ import com.ibm.icu.impl.UResource;
* PluralRules and DecimalFormat. It is package-protected as it is not meant for public use.
*/
class QuantityFormatter {
/**
* Plural forms in index order: "other", "zero", "one", "two", "few", "many"
* "other" must be first.
*/
private static final int getPluralIndex(CharSequence pluralForm) {
switch (pluralForm.length()) {
case 3:
if ("one".contentEquals(pluralForm)) {
return 2;
} else if ("two".contentEquals(pluralForm)) {
return 3;
} else if ("few".contentEquals(pluralForm)) {
return 4;
}
break;
case 4:
if ("many".contentEquals(pluralForm)) {
return 5;
} else if ("zero".contentEquals(pluralForm)) {
return 1;
}
break;
case 5:
if ("other".contentEquals(pluralForm)) {
return 0;
}
break;
default:
break;
}
return -1;
}
private static final int INDEX_COUNT = 6;
private final SimplePatternFormatter[] templates = new SimplePatternFormatter[INDEX_COUNT];
private final SimplePatternFormatter[] templates =
new SimplePatternFormatter[StandardPluralCategories.COUNT];
public QuantityFormatter() {}
@ -67,49 +34,18 @@ class QuantityFormatter {
* if template has more than just the {0} placeholder.
*/
public void addIfAbsent(CharSequence variant, String template) {
addIfAbsent(variant, template, null);
}
/**
* Adds a template if there is none yet for the plural form.
* This version only calls UResource.Value.getString()
* if there is no template yet for the plural form.
*
* @param variant the plural variant, e.g "zero", "one", "two", "few", "many", "other"
* @param template the text for that plural variant with "{0}" as the quantity. For
* example, in English, the template for the "one" variant may be "{0} apple" while the
* template for the "other" variant may be "{0} apples"
* @throws IllegalArgumentException if variant is not recognized or
* if template has more than just the {0} placeholder.
*/
public void addIfAbsent(CharSequence variant, UResource.Value template) {
addIfAbsent(variant, null, template);
}
private void addIfAbsent(CharSequence variant, String template, UResource.Value templateValue) {
int idx = getPluralIndex(variant);
if (idx < 0) {
throw new IllegalArgumentException(variant.toString());
}
int idx = StandardPluralCategories.getIndex(variant);
if (templates[idx] != null) {
return;
}
if (template == null) {
template = templateValue.getString();
}
SimplePatternFormatter newT = SimplePatternFormatter.compile(template);
if (newT.getPlaceholderCount() > 1) {
throw new IllegalArgumentException(
"Extra placeholders: " + template);
}
templates[idx] = newT;
templates[idx] = SimplePatternFormatter.compileMinMaxPlaceholders(template, 0, 1);
}
/**
* @return true if this object has at least the "other" variant
*/
public boolean isValid() {
return templates[0] != null;
return templates[StandardPluralCategories.OTHER_INDEX] != null;
}
/**
@ -122,7 +58,7 @@ class QuantityFormatter {
*/
public String format(double quantity, NumberFormat numberFormat, PluralRules pluralRules) {
String formatStr = numberFormat.format(quantity);
String variant = computeVariant(quantity, numberFormat, pluralRules);
String variant = pluralRules.select(quantity, numberFormat);
return getByVariant(variant).format(formatStr);
}
@ -133,15 +69,9 @@ class QuantityFormatter {
*/
public SimplePatternFormatter getByVariant(CharSequence variant) {
assert isValid();
int idx = getPluralIndex(variant);
SimplePatternFormatter template = templates[idx < 0 ? 0 : idx];
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);
int idx = StandardPluralCategories.getIndexOrOtherIndex(variant);
SimplePatternFormatter template = templates[idx];
return (template == null && idx != StandardPluralCategories.OTHER_INDEX) ?
templates[StandardPluralCategories.OTHER_INDEX] : template;
}
}