blob: e95a165094694b74eb5fbfbceb9e70208e12d62e [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2013-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
import java.util.HashMap;
import java.util.Map;
import com.ibm.icu.impl.SimplePatternFormatter;
/**
* QuantityFormatter represents an unknown quantity of something and formats a known quantity
* in terms of that something. For example, a QuantityFormatter that represents X apples may
* format 1 as "1 apple" and 3 as "3 apples"
* <p>
* QuanitityFormatter appears here instead of in com.ibm.icu.impl because it depends on
* PluralRules and DecimalFormat. It is package-protected as it is not meant for public use.
*/
class QuantityFormatter {
private static final Map<String, Integer> INDEX_MAP = new HashMap<String, Integer>();
private static final int MAX_INDEX;
static {
int idx = 0;
// Other must be first.
INDEX_MAP.put("other", idx++);
INDEX_MAP.put("zero", idx++);
INDEX_MAP.put("one", idx++);
INDEX_MAP.put("two", idx++);
INDEX_MAP.put("few", idx++);
INDEX_MAP.put("many", idx++);
MAX_INDEX = idx;
}
/**
* Builder builds a QuantityFormatter.
*
* @author rocketman
*/
static class Builder {
private SimplePatternFormatter[] templates;
/**
* Adds a template.
* @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"
* @return a reference to this Builder for chaining.
* @throws IllegalArgumentException if variant is not recognized or
* if template has more than just the {0} placeholder.
*/
public Builder add(String variant, String template) {
ensureCapacity();
Integer idx = INDEX_MAP.get(variant);
if (idx == null) {
throw new IllegalArgumentException(variant);
}
SimplePatternFormatter newT = SimplePatternFormatter.compile(template);
if (newT.getPlaceholderCount() > 1) {
throw new IllegalArgumentException(
"Extra placeholders: " + template);
}
templates[idx.intValue()] = newT;
return this;
}
/**
* 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 leaves this builder in its current state.
*/
public QuantityFormatter build() {
if (templates == null || templates[0] == null) {
throw new IllegalStateException("At least other variant must be set.");
}
QuantityFormatter result = new QuantityFormatter(templates);
templates = null;
return result;
}
/**
* Resets this builder to its initial state.
*/
public Builder reset() {
templates = null;
return this;
}
private void ensureCapacity() {
if (templates == null) {
templates = new SimplePatternFormatter[MAX_INDEX];
}
}
}
private final SimplePatternFormatter[] templates;
private QuantityFormatter(SimplePatternFormatter[] templates) {
this.templates = templates;
}
/**
* Format formats a quantity with this object.
* @param quantity the quantity to be formatted
* @param numberFormat used to actually format the quantity.
* @param pluralRules uses the quantity and the numberFormat to determine what plural
* variant to use for fetching the formatting template.
* @return the formatted string e.g '3 apples'
*/
public String format(double quantity, NumberFormat numberFormat, PluralRules pluralRules) {
String formatStr = numberFormat.format(quantity);
String variant = computeVariant(quantity, numberFormat, pluralRules);
return getByVariant(variant).format(formatStr);
}
/**
* 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);
}
}