17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 2007-2015, International Business Machines Corporation and
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.Utility;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.FixedDecimal;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.PluralType;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale.Category;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>PluralFormat</code> supports the creation of internationalized
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * messages with plural inflection. It is based on <i>plural
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * selection</i>, i.e. the caller specifies messages for each
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * plural case that can appear in the user's language and the
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>PluralFormat</code> selects the appropriate message based on
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the number.
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4>The Problem of Plural Forms in Internationalized Messages</h4>
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Different languages have different ways to inflect
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * plurals. Creating internationalized messages that include plural
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * forms is only feasible when the framework is able to handle plural
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * forms of <i>all</i> languages correctly. <code>ChoiceFormat</code>
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * doesn't handle this well, because it attaches a number interval to
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * each message and selects the message whose interval contains a
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * given number. This can only handle a finite number of
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * intervals. But in some languages, like Polish, one plural case
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * applies to infinitely many intervals (e.g., the paucal case applies to
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * numbers ending with 2, 3, or 4 except those ending with 12, 13, or
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 14). Thus <code>ChoiceFormat</code> is not adequate.
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>PluralFormat</code> deals with this by breaking the problem
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * into two parts:
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul>
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>It uses <code>PluralRules</code> that can define more complex
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     conditions for a plural case than just a single interval. These plural
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     rules define both what plural cases exist in a language, and to
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     which numbers these cases apply.
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>It provides predefined plural rules for many languages. Thus, the programmer
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     need not worry about the plural cases of a language and
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     does not have to define the plural cases; they can simply
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     use the predefined keywords. The whole plural formatting of messages can
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     be done using localized patterns from resource bundles. For predefined plural
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     rules, see the CLDR <i>Language Plural Rules</i> page at
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul>
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4>Usage of <code>PluralFormat</code></h4>
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Note: Typically, plural formatting is done via <code>MessageFormat</code>
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with a <code>plural</code> argument type,
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * rather than using a stand-alone <code>PluralFormat</code>.
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This discussion assumes that you use <code>PluralFormat</code> with
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a predefined set of plural rules. You can create one using one of
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the constructors that takes a <code>ULocale</code> object. To
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * specify the message pattern, you can either pass it to the
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * constructor or set it explicitly using the
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>applyPattern()</code> method. The <code>format()</code>
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method takes a number object and selects the message of the
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * matching plural case. This message will be returned.
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h5>Patterns and Their Interpretation</h5>
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The pattern text defines the message output for each plural case of the
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * specified locale. Syntax:
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote><pre>
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pluralStyle = [offsetValue] (selector '{' message '}')+
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * offsetValue = "offset:" number
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * selector = explicitValue | keyword
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * explicitValue = '=' number  // adjacent, no white space in between
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * message: see {@link MessageFormat}
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre></blockquote>
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Pattern_White_Space between syntax elements is ignored, except
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * between the {curly braces} and their sub-message,
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and between the '=' and the number of an explicitValue.
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are 6 predefined case keywords in CLDR/ICU - 'zero', 'one', 'two', 'few', 'many' and
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 'other'. You always have to define a message text for the default plural case
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "<code>other</code>" which is contained in every rule set.
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If you do not specify a message text for a particular plural case, the
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * message text of the plural case "<code>other</code>" gets assigned to this
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * plural case.
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * When formatting, the input number is first matched against the explicitValue clauses.
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If there is no exact-number match, then a keyword is selected by calling
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the <code>PluralRules</code> with the input number <em>minus the offset</em>.
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (The offset defaults to 0 if it is omitted from the pattern string.)
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If there is no clause with that keyword, then the "other" clauses is returned.
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * An unquoted pound sign (<code>#</code>) in the selected sub-message
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * itself (i.e., outside of arguments nested in the sub-message)
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is replaced by the input number minus the offset.
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The number-minus-offset value is formatted using a
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>NumberFormat</code> for the <code>PluralFormat</code>'s locale. If you
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * need special number formatting, you have to use a <code>MessageFormat</code>
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and explicitly specify a <code>NumberFormat</code> argument.
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <strong>Note:</strong> That argument is formatting without subtracting the offset!
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If you need a custom format and have a non-zero offset, then you need to pass the
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * number-minus-offset value as a separate parameter.
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For a usage example, see the {@link MessageFormat} class documentation.
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4>Defining Custom Plural Rules</h4>
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>If you need to use <code>PluralFormat</code> with custom rules, you can
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * create a <code>PluralRules</code> object and pass it to
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>PluralFormat</code>'s constructor. If you also specify a locale in this
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * constructor, this locale will be used to format the number in the message
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * texts.
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p><p>
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For more information about <code>PluralRules</code>, see
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link PluralRules}.
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </p>
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author tschumann (Tim Schumann)
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class PluralFormat extends UFormat {
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 1L;
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The locale used for standard number formatting and getting the predefined
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * plural rules (if they were not defined explicitely).
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @serial
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ULocale ulocale = null;
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The plural rules used for plural selection.
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @serial
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private PluralRules pluralRules = null;
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The applied pattern string.
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @serial
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String pattern = null;
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The MessagePattern which contains the parsed structure of the pattern string.
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    transient private MessagePattern msgPattern;
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Obsolete with use of MessagePattern since ICU 4.8. Used to be:
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The format messages for each plural case. It is a mapping:
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  <code>String</code>(plural case keyword) --&gt; <code>String</code>
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  (message for this plural case).
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @serial
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, String> parsedValues = null;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This <code>NumberFormat</code> is used for the standard formatting of
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the number inserted into the message.
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @serial
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private NumberFormat numberFormat = null;
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The offset to subtract before invoking plural rules.
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    transient private double offset = 0;
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This locale will be used to get the set of plural rules and for standard
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * number formatting.
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat() {
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given locale.
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>PluralFormat</code> will be configured with
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale) {
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, PluralType.CARDINAL, ulocale, null);
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given JDK locale.
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale the <code>PluralFormat</code> will be configured with
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 54
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(Locale locale) {
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this(ULocale.forLocale(locale));
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the default <code>FORMAT</code> locale.
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rules defines the behavior of the <code>PluralFormat</code>
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        object.
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(PluralRules rules) {
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the given locale.
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the default number formatting will be done using this
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        locale.
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rules defines the behavior of the <code>PluralFormat</code>
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        object.
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale, PluralRules rules) {
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(rules, PluralType.CARDINAL, ulocale, null);
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the given locale.
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale the default number formatting will be done using this
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        locale.
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rules defines the behavior of the <code>PluralFormat</code>
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        object.
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 54
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(Locale locale, PluralRules rules) {
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this(ULocale.forLocale(locale), rules);
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new <code>PluralFormat</code> for the plural type.
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the given locale.
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the default number formatting will be done using this
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        locale.
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale, PluralType type) {
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, type, ulocale, null);
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new <code>PluralFormat</code> for the plural type.
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the given JDK locale.
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale the default number formatting will be done using this
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        locale.
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 54
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(Locale locale, PluralType type) {
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this(ULocale.forLocale(locale), type);
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string.
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The default <code>FORMAT</code> locale will be used to get the set of plural rules and for
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * standard number formatting.
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  pattern the pattern for this <code>PluralFormat</code>.
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(String pattern) {
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string and
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * locale.
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The locale will be used to get the set of plural rules and for
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * standard number formatting.
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.pluralformat.PluralFormatSample:---PluralFormatExample}
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>PluralFormat</code> will be configured with
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  pattern the pattern for this <code>PluralFormat</code>.
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale, String pattern) {
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, PluralType.CARDINAL, ulocale, null);
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules and a
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * pattern.
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The standard number formatting will be done using the default <code>FORMAT</code> locale.
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rules defines the behavior of the <code>PluralFormat</code>
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        object.
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  pattern the pattern for this <code>PluralFormat</code>.
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(PluralRules rules, String pattern) {
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules, a
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * pattern and a locale.
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>PluralFormat</code> will be configured with
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rules defines the behavior of the <code>PluralFormat</code>
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        object.
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  pattern the pattern for this <code>PluralFormat</code>.
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale, PluralRules rules, String pattern) {
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(rules, PluralType.CARDINAL, ulocale, null);
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new <code>PluralFormat</code> for a plural type, a
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * pattern and a locale.
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>PluralFormat</code> will be configured with
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  pattern the pattern for this <code>PluralFormat</code>.
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralFormat(ULocale ulocale, PluralType type, String pattern) {
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, type, ulocale, null);
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Creates a new <code>PluralFormat</code> for a plural type, a
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * pattern and a locale.
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>PluralFormat</code> will be configured with
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        rules for this locale. This locale will also be used for standard
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        number formatting.
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The plural type (e.g., cardinal or ordinal).
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern the pattern for this <code>PluralFormat</code>.
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param numberFormat The number formatter to use.
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*package*/ PluralFormat(ULocale ulocale, PluralType type, String pattern, NumberFormat numberFormat) {
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, type, ulocale, numberFormat);
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        applyPattern(pattern);
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initializes the <code>PluralRules</code> object.
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Postcondition:<br/>
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <code>ulocale</code>    :  is <code>locale</code><br/>
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <code>pluralRules</code>:  if <code>rules</code> != <code>null</code>
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                              it's set to rules, otherwise it is the
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                              predefined plural rule set for the locale
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                              <code>ulocale</code>.<br/>
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <code>parsedValues</code>: is <code>null</code><br/>
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <code>pattern</code>:      is <code>null</code><br/>
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *   <code>numberFormat</code>: a <code>NumberFormat</code> for the locale
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                              <code>ulocale</code>.
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void init(PluralRules rules, PluralType type, ULocale locale, NumberFormat numberFormat) {
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ulocale = locale;
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pluralRules = (rules == null) ? PluralRules.forLocale(ulocale, type)
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                      : rules;
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        resetPattern();
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.numberFormat = (numberFormat == null) ? NumberFormat.getInstance(ulocale) : numberFormat;
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void resetPattern() {
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pattern = null;
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(msgPattern != null) {
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            msgPattern.clear();
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        offset = 0;
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the pattern used by this plural format.
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The method parses the pattern and creates a map of format strings
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * for the plural rules.
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Patterns and their interpretation are specified in the class description.
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern the pattern for this plural format.
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the pattern is invalid.
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void applyPattern(String pattern) {
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.pattern = pattern;
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (msgPattern == null) {
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            msgPattern = new MessagePattern();
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            msgPattern.parsePluralStyle(pattern);
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            offset = msgPattern.getPluralOffset(0);
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch(RuntimeException e) {
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            resetPattern();
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw e;
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the pattern for this PluralFormat.
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the pattern string
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.2
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toPattern() {
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return pattern;
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Finds the PluralFormat sub-message for the given number, or the "other" sub-message.
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pattern A MessagePattern.
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param partIndex the index of the first PluralFormat argument style part.
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param selector the PluralSelector for mapping the number (minus offset) to a keyword.
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param context worker object for the selector.
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param number a number to be matched to one of the PluralFormat argument's explicit values,
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        or mapped via the PluralSelector.
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the sub-message start part index.
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*package*/ static int findSubMessage(
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern pattern, int partIndex,
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PluralSelector selector, Object context, double number) {
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int count=pattern.countParts();
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        double offset;
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        MessagePattern.Part part=pattern.getPart(partIndex);
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(part.getType().hasNumericValue()) {
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            offset=pattern.getNumericValue(part);
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++partIndex;
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            offset=0;
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // The keyword is null until we need to match against a non-explicit, not-"other" value.
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Then we get the keyword from the selector.
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // (In other words, we never call the selector if we match against an explicit value,
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // or if the only non-explicit keyword is "other".)
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String keyword=null;
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // When we find a match, we set msgStart>0 and also set this boolean to true
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // to avoid matching the keyword again (duplicates are allowed)
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // while we continue to look for an explicit-value match.
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean haveKeywordMatch=false;
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // msgStart is 0 until we find any appropriate sub-message.
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We remember the first "other" sub-message if we have not seen any
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // appropriate sub-message before.
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We remember the first matching-keyword sub-message if we have not seen
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // one of those before.
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // (The parser allows [does not check for] duplicate keywords.
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We just have to make sure to take the first one.)
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We avoid matching the keyword twice by also setting haveKeywordMatch=true
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // at the first keyword match.
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // We keep going until we find an explicit-value match or reach the end of the plural style.
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int msgStart=0;
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // until ARG_LIMIT or end of plural-only pattern.
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        do {
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            part=pattern.getPart(partIndex++);
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part.Type type=part.getType();
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(type==MessagePattern.Part.Type.ARG_LIMIT) {
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            assert type==MessagePattern.Part.Type.ARG_SELECTOR;
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if(pattern.getPartType(partIndex).hasNumericValue()) {
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // explicit value like "=2"
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                part=pattern.getPart(partIndex++);
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(number==pattern.getNumericValue(part)) {
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // matches explicit value
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return partIndex;
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if(!haveKeywordMatch) {
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // plural keyword like "few" or "other"
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Compare "other" first and call the selector if this is not "other".
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if(pattern.partSubstringMatches(part, "other")) {
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if(msgStart==0) {
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        msgStart=partIndex;
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if(keyword!=null && keyword.equals("other")) {
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // This is the first "other" sub-message,
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // and the selected keyword is also "other".
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Do not match "other" again.
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            haveKeywordMatch=true;
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if(keyword==null) {
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        keyword=selector.select(context, number-offset);
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if(msgStart!=0 && keyword.equals("other")) {
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // We have already seen an "other" sub-message.
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Do not match "other" again.
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            haveKeywordMatch=true;
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Skip keyword matching but do getLimitPartIndex().
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if(!haveKeywordMatch && pattern.partSubstringMatches(part, keyword)) {
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // keyword matches
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        msgStart=partIndex;
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Do not match this keyword again.
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        haveKeywordMatch=true;
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            partIndex=pattern.getLimitPartIndex(partIndex);
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } while(++partIndex<count);
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return msgStart;
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Interface for selecting PluralFormat keywords for numbers.
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The PluralRules class was intended to implement this interface,
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but there is no public API that uses a PluralSelector,
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * only MessageFormat and PluralFormat have PluralSelector implementations.
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Therefore, PluralRules is not marked to implement this non-public interface,
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to avoid confusing users.
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*package*/ interface PluralSelector {
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Given a number, returns the appropriate PluralFormat keyword.
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param context worker object for the selector.
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @param number The number to be plural-formatted.
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @return The selected PluralFormat keyword.
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String select(Object context, double number);
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // See PluralSelector:
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // We could avoid this adapter class if we made PluralSelector public
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // (or at least publicly visible) and had PluralRules implement PluralSelector.
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final class PluralSelectorAdapter implements PluralSelector {
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String select(Object context, double number) {
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            FixedDecimal dec = (FixedDecimal) context;
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            assert dec.source == number;
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return pluralRules.select(dec);
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    transient private PluralSelectorAdapter pluralRulesWrapper = new PluralSelectorAdapter();
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Formats a plural message for a given number.
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param number a number for which the plural message should be formatted.
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        If no pattern has been applied to this
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        <code>PluralFormat</code> object yet, the formatted number will
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        be returned.
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the string containing the formatted plural message.
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final String format(double number) {
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return format(number, number);
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Formats a plural message for a given number and appends the formatted
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * message to the given <code>StringBuffer</code>.
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param number a number object (instance of <code>Number</code> for which
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        the plural message should be formatted. If no pattern has been
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        applied to this <code>PluralFormat</code> object yet, the
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        formatted number will be returned.
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        Note: If this object is not an instance of <code>Number</code>,
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *              the <code>toAppendTo</code> will not be modified.
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toAppendTo the formatted message will be appended to this
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        <code>StringBuffer</code>.
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos will be ignored by this method.
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the string buffer passed in as toAppendTo, with formatted text
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         appended.
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if number is not an instance of Number
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public StringBuffer format(Object number, StringBuffer toAppendTo,
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            FieldPosition pos) {
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!(number instanceof Number)) {
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("'" + number + "' is not a Number");
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Number numberObject = (Number) number;
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        toAppendTo.append(format(numberObject, numberObject.doubleValue()));
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return toAppendTo;
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String format(Number numberObject, double number) {
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If no pattern was applied, return the formatted number.
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (msgPattern == null || msgPattern.countParts() == 0) {
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return numberFormat.format(numberObject);
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Get the appropriate sub-message.
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Select it based on the formatted number-offset.
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        double numberMinusOffset = number - offset;
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String numberString;
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (offset == 0) {
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            numberString = numberFormat.format(numberObject);  // could be BigDecimal etc.
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            numberString = numberFormat.format(numberMinusOffset);
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        FixedDecimal dec;
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(numberFormat instanceof DecimalFormat) {
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            dec = ((DecimalFormat) numberFormat).getFixedDecimal(numberMinusOffset);
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            dec = new FixedDecimal(numberMinusOffset);
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, dec, number);
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Replace syntactic # signs in the top level of this sub-message
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // (not in nested arguments) with the formatted number-offset.
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder result = null;
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int prevIndex = msgPattern.getPart(partIndex).getLimit();
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (;;) {
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part part = msgPattern.getPart(++partIndex);
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part.Type type = part.getType();
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int index = part.getIndex();
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (type == MessagePattern.Part.Type.MSG_LIMIT) {
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (result == null) {
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return pattern.substring(prevIndex, index);
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return result.append(pattern, prevIndex, index).toString();
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (type == MessagePattern.Part.Type.REPLACE_NUMBER ||
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // JDK compatibility mode: Remove SKIP_SYNTAX.
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        (type == MessagePattern.Part.Type.SKIP_SYNTAX && msgPattern.jdkAposMode())) {
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (result == null) {
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result = new StringBuilder();
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(pattern, prevIndex, index);
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (type == MessagePattern.Part.Type.REPLACE_NUMBER) {
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.append(numberString);
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevIndex = part.getLimit();
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (type == MessagePattern.Part.Type.ARG_START) {
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (result == null) {
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result = new StringBuilder();
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.append(pattern, prevIndex, index);
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevIndex = index;
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                partIndex = msgPattern.getLimitPartIndex(partIndex);
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                index = msgPattern.getPart(partIndex).getLimit();
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                MessagePattern.appendReducedApostrophes(pattern, prevIndex, index, result);
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevIndex = index;
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method is not yet supported by <code>PluralFormat</code>.
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param text the string to be parsed.
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param parsePosition defines the position where parsing is to begin,
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and upon return, the position where parsing left off.  If the position
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has not changed upon return, then parsing failed.
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return nothing because this method is not yet implemented.
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws UnsupportedOperationException will always be thrown by this method.
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Number parse(String text, ParsePosition parsePosition) {
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // You get number ranges from this. You can't get an exact number.
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new UnsupportedOperationException();
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method is not yet supported by <code>PluralFormat</code>.
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param source the string to be parsed.
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos defines the position where parsing is to begin,
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and upon return, the position where parsing left off.  If the position
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has not changed upon return, then parsing failed.
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return nothing because this method is not yet implemented.
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws UnsupportedOperationException will always be thrown by this method.
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object parseObject(String source, ParsePosition pos) {
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new UnsupportedOperationException();
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method returns the PluralRules type found from parsing.
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param source the string to be parsed.
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos defines the position where parsing is to begin,
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and upon return, the position where parsing left off.  If the position
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is a negative index, then parsing failed.
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return Returns the PluralRules type. For example, it could be "zero", "one", "two", "few", "many" or "other")
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*package*/ String parseType(String source, RbnfLenientScanner scanner, FieldPosition pos) {
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If no pattern was applied, return null.
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (msgPattern == null || msgPattern.countParts() == 0) {
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pos.setBeginIndex(-1);
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pos.setEndIndex(-1);
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int partIndex = 0;
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int currMatchIndex;
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int count=msgPattern.countParts();
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int startingAt = pos.getBeginIndex();
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (startingAt < 0) {
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            startingAt = 0;
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // The keyword is null until we need to match against a non-explicit, not-"other" value.
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Then we get the keyword from the selector.
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // (In other words, we never call the selector if we match against an explicit value,
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // or if the only non-explicit keyword is "other".)
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String keyword = null;
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String matchedWord = null;
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int matchedIndex = -1;
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // until the end of the plural-only pattern.
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (partIndex < count) {
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part partSelector=msgPattern.getPart(partIndex++);
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (partSelector.getType() != MessagePattern.Part.Type.ARG_SELECTOR) {
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Bad format
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part partStart=msgPattern.getPart(partIndex++);
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (partStart.getType() != MessagePattern.Part.Type.MSG_START) {
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Bad format
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            MessagePattern.Part partLimit=msgPattern.getPart(partIndex++);
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (partLimit.getType() != MessagePattern.Part.Type.MSG_LIMIT) {
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Bad format
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue;
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String currArg = pattern.substring(partStart.getLimit(), partLimit.getIndex());
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (scanner != null) {
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us.
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int[] scannerMatchResult = scanner.findText(source, currArg, startingAt);
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                currMatchIndex = scannerMatchResult[0];
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else {
755f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                currMatchIndex = source.indexOf(currArg, startingAt);
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && (matchedWord == null || currArg.length() > matchedWord.length())) {
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                matchedIndex = currMatchIndex;
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                matchedWord = currArg;
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                keyword = pattern.substring(partStart.getLimit(), partLimit.getIndex());
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (keyword != null) {
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pos.setBeginIndex(matchedIndex);
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pos.setEndIndex(matchedIndex + matchedWord.length());
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return keyword;
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Not found!
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pos.setBeginIndex(-1);
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pos.setEndIndex(-1);
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the locale used by this <code>PluraFormat</code> object.
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Note: Calling this method resets this <code>PluraFormat</code> object,
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     i.e., a pattern that was applied previously will be removed,
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     and the NumberFormat is set to the default number format for
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     the locale.  The resulting format behaves the same as one
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     constructed from {@link #PluralFormat(ULocale, PluralRules.PluralType)}
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     with PluralType.CARDINAL.
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ulocale the <code>ULocale</code> used to configure the
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     formatter. If <code>ulocale</code> is <code>null</code>, the
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *     default <code>FORMAT</code> locale will be used.
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated ICU 50 This method clears the pattern and might create
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *             a different kind of PluralRules instance;
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *             use one of the constructors to create a new instance instead.
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setLocale(ULocale ulocale) {
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (ulocale == null) {
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ulocale = ULocale.getDefault(Category.FORMAT);
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        init(null, PluralType.CARDINAL, ulocale, null);
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Sets the number format used by this formatter.  You only need to
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * call this if you want a different number format than the default
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * formatter for the locale.
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param format the number format to use.
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setNumberFormat(NumberFormat format) {
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        numberFormat = format;
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(Object rhs) {
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(this == rhs) {
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(rhs == null || getClass() != rhs.getClass()) {
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PluralFormat pf = (PluralFormat)rhs;
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Utility.objectEquals(ulocale, pf.ulocale) &&
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Utility.objectEquals(pluralRules, pf.pluralRules) &&
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Utility.objectEquals(msgPattern, pf.msgPattern) &&
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Utility.objectEquals(numberFormat, pf.numberFormat);
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns true if this equals the provided PluralFormat.
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param rhs the PluralFormat to compare against
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if this equals rhs
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(PluralFormat rhs) {
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return equals((Object)rhs);
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int hashCode() {
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return pluralRules.hashCode() ^ parsedValues.hashCode();
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 3.8
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toString() {
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder buf = new StringBuilder();
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append("locale=" + ulocale);
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(", rules='" + pluralRules + "'");
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(", pattern='" + pattern + "'");
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(", format='" + numberFormat + "'");
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buf.toString();
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        in.defaultReadObject();
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        pluralRulesWrapper = new PluralSelectorAdapter();
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Ignore the parsedValues from an earlier class version (before ICU 4.8)
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // and rebuild the msgPattern.
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        parsedValues = null;
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (pattern != null) {
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            applyPattern(pattern);
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
874