diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/SimplePatternFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/SimplePatternFormatter.java index e7212c7d7e3..7a54f2f494b 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/SimplePatternFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/SimplePatternFormatter.java @@ -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); - } /** diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/ListFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/text/ListFormatter.java index 9b63216128a..86ad07968fc 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/ListFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/ListFormatter.java @@ -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 localeToData = new HashMap(); - 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 cache = new SimpleCache(); @@ -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); } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java index 601665f632f..cf0dc24e1c2 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java @@ -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 styleToPatterns = + cacheData.unitToStyleToPatterns.get(unit); + if (styleToPatterns == null) { + styleToPatterns = + new EnumMap(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 styleToCountToFormat = - cacheData.unitToStyleToCountToFormat.get(unit); - if (styleToCountToFormat == null) { - styleToCountToFormat = - new EnumMap(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 styleToCountToFormat = - cache.unitToStyleToCountToFormat.get(unit); - QuantityFormatter countToFormat = styleToCountToFormat.get(width); - if (countToFormat != null) { - return countToFormat; + Map 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 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 styleToPerUnitPattern = - unitToStyleToPerUnitPattern.get(unit); - if (styleToPerUnitPattern == null) { - styleToPerUnitPattern = - new EnumMap(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> unitToStyleToCountToFormat = - new HashMap>(); - final Map> unitToStyleToPerUnitPattern = - new HashMap>(); + /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */ + final Map> unitToStyleToPatterns = + new HashMap>(); final EnumMap styleToPerPattern = new EnumMap(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); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java index 816cc6ae4c8..ce664113942 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java @@ -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 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 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. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralSamples.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralSamples.java index a7ef98f7dc3..a58b154c8a9 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralSamples.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralSamples.java @@ -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 KEYWORD_COMPARATOR = new Comparator () { - 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. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/QuantityFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/text/QuantityFormatter.java index b513347cfda..baf34f7d197 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/QuantityFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/QuantityFormatter.java @@ -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; } }