1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2013-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.text; 10 11 import java.text.FieldPosition; 12 13 import com.ibm.icu.impl.SimpleFormatterImpl; 14 import com.ibm.icu.impl.StandardPlural; 15 16 /** 17 * QuantityFormatter represents an unknown quantity of something and formats a known quantity 18 * in terms of that something. For example, a QuantityFormatter that represents X apples may 19 * format 1 as "1 apple" and 3 as "3 apples" 20 * <p> 21 * QuanitityFormatter appears here instead of in com.ibm.icu.impl because it depends on 22 * PluralRules and DecimalFormat. It is package-protected as it is not meant for public use. 23 */ 24 class QuantityFormatter { 25 private final SimpleFormatter[] templates = 26 new SimpleFormatter[StandardPlural.COUNT]; 27 QuantityFormatter()28 public QuantityFormatter() {} 29 30 /** 31 * Adds a template if there is none yet for the plural form. 32 * 33 * @param variant the plural variant, e.g "zero", "one", "two", "few", "many", "other" 34 * @param template the text for that plural variant with "{0}" as the quantity. For 35 * example, in English, the template for the "one" variant may be "{0} apple" while the 36 * template for the "other" variant may be "{0} apples" 37 * @throws IllegalArgumentException if variant is not recognized or 38 * if template has more than just the {0} placeholder. 39 */ addIfAbsent(CharSequence variant, String template)40 public void addIfAbsent(CharSequence variant, String template) { 41 int idx = StandardPlural.indexFromString(variant); 42 if (templates[idx] != null) { 43 return; 44 } 45 templates[idx] = SimpleFormatter.compileMinMaxArguments(template, 0, 1); 46 } 47 48 /** 49 * @return true if this object has at least the "other" variant 50 */ isValid()51 public boolean isValid() { 52 return templates[StandardPlural.OTHER_INDEX] != null; 53 } 54 55 /** 56 * Format formats a number with this object. 57 * @param number the number to be formatted 58 * @param numberFormat used to actually format the number. 59 * @param pluralRules uses the number and the numberFormat to determine what plural 60 * variant to use for fetching the formatting template. 61 * @return the formatted string e.g '3 apples' 62 */ format(double number, NumberFormat numberFormat, PluralRules pluralRules)63 public String format(double number, NumberFormat numberFormat, PluralRules pluralRules) { 64 String formatStr = numberFormat.format(number); 65 StandardPlural p = selectPlural(number, numberFormat, pluralRules); 66 SimpleFormatter formatter = templates[p.ordinal()]; 67 if (formatter == null) { 68 formatter = templates[StandardPlural.OTHER_INDEX]; 69 assert formatter != null; 70 } 71 return formatter.format(formatStr); 72 } 73 74 /** 75 * Gets the SimpleFormatter for a particular variant. 76 * @param variant "zero", "one", "two", "few", "many", "other" 77 * @return the SimpleFormatter 78 */ getByVariant(CharSequence variant)79 public SimpleFormatter getByVariant(CharSequence variant) { 80 assert isValid(); 81 int idx = StandardPlural.indexOrOtherIndexFromString(variant); 82 SimpleFormatter template = templates[idx]; 83 return (template == null && idx != StandardPlural.OTHER_INDEX) ? 84 templates[StandardPlural.OTHER_INDEX] : template; 85 } 86 87 // The following methods live here so that class PluralRules does not depend on number formatting, 88 // and the SimpleFormatter does not depend on FieldPosition. 89 90 /** 91 * Selects the standard plural form for the number/formatter/rules. 92 */ selectPlural(double number, NumberFormat numberFormat, PluralRules rules)93 public static StandardPlural selectPlural(double number, NumberFormat numberFormat, PluralRules rules) { 94 String pluralKeyword; 95 if (numberFormat instanceof DecimalFormat) { 96 pluralKeyword = rules.select(((DecimalFormat) numberFormat).getFixedDecimal(number)); 97 } else { 98 pluralKeyword = rules.select(number); 99 } 100 return StandardPlural.orOtherFromString(pluralKeyword); 101 } 102 103 /** 104 * Formats the pattern with the value and adjusts the FieldPosition. 105 */ format(String compiledPattern, CharSequence value, StringBuilder appendTo, FieldPosition pos)106 public static StringBuilder format(String compiledPattern, CharSequence value, 107 StringBuilder appendTo, FieldPosition pos) { 108 int[] offsets = new int[1]; 109 SimpleFormatterImpl.formatAndAppend(compiledPattern, appendTo, offsets, value); 110 if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) { 111 if (offsets[0] >= 0) { 112 pos.setBeginIndex(pos.getBeginIndex() + offsets[0]); 113 pos.setEndIndex(pos.getEndIndex() + offsets[0]); 114 } else { 115 pos.setBeginIndex(0); 116 pos.setEndIndex(0); 117 } 118 } 119 return appendTo; 120 } 121 } 122