Home | History | Annotate | Download | only in text
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5  *******************************************************************************
      6  * Copyright (C) 2007-2016, International Business Machines Corporation and
      7  * others. All Rights Reserved.
      8  *******************************************************************************
      9  */
     10 
     11 package android.icu.text;
     12 
     13 import java.io.IOException;
     14 import java.io.ObjectInputStream;
     15 import java.text.FieldPosition;
     16 import java.text.ParsePosition;
     17 import java.util.Locale;
     18 import java.util.Map;
     19 
     20 import android.icu.impl.Utility;
     21 import android.icu.text.PluralRules.FixedDecimal;
     22 import android.icu.text.PluralRules.IFixedDecimal;
     23 import android.icu.text.PluralRules.PluralType;
     24 import android.icu.util.ULocale;
     25 import android.icu.util.ULocale.Category;
     26 
     27 /**
     28  * <code>PluralFormat</code> supports the creation of internationalized
     29  * messages with plural inflection. It is based on <i>plural
     30  * selection</i>, i.e. the caller specifies messages for each
     31  * plural case that can appear in the user's language and the
     32  * <code>PluralFormat</code> selects the appropriate message based on
     33  * the number.
     34  *
     35  * <h3>The Problem of Plural Forms in Internationalized Messages</h3>
     36  * <p>
     37  * Different languages have different ways to inflect
     38  * plurals. Creating internationalized messages that include plural
     39  * forms is only feasible when the framework is able to handle plural
     40  * forms of <i>all</i> languages correctly. <code>ChoiceFormat</code>
     41  * doesn't handle this well, because it attaches a number interval to
     42  * each message and selects the message whose interval contains a
     43  * given number. This can only handle a finite number of
     44  * intervals. But in some languages, like Polish, one plural case
     45  * applies to infinitely many intervals (e.g., the paucal case applies to
     46  * numbers ending with 2, 3, or 4 except those ending with 12, 13, or
     47  * 14). Thus <code>ChoiceFormat</code> is not adequate.
     48  * <p>
     49  * <code>PluralFormat</code> deals with this by breaking the problem
     50  * into two parts:
     51  * <ul>
     52  * <li>It uses <code>PluralRules</code> that can define more complex
     53  *     conditions for a plural case than just a single interval. These plural
     54  *     rules define both what plural cases exist in a language, and to
     55  *     which numbers these cases apply.
     56  * <li>It provides predefined plural rules for many languages. Thus, the programmer
     57  *     need not worry about the plural cases of a language and
     58  *     does not have to define the plural cases; they can simply
     59  *     use the predefined keywords. The whole plural formatting of messages can
     60  *     be done using localized patterns from resource bundles. For predefined plural
     61  *     rules, see the CLDR <i>Language Plural Rules</i> page at
     62  *    http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
     63  * </ul>
     64  *
     65  * <h4>Usage of <code>PluralFormat</code></h4>
     66  * <p>Note: Typically, plural formatting is done via <code>MessageFormat</code>
     67  * with a <code>plural</code> argument type,
     68  * rather than using a stand-alone <code>PluralFormat</code>.
     69  * <p>
     70  * This discussion assumes that you use <code>PluralFormat</code> with
     71  * a predefined set of plural rules. You can create one using one of
     72  * the constructors that takes a <code>ULocale</code> object. To
     73  * specify the message pattern, you can either pass it to the
     74  * constructor or set it explicitly using the
     75  * <code>applyPattern()</code> method. The <code>format()</code>
     76  * method takes a number object and selects the message of the
     77  * matching plural case. This message will be returned.
     78  *
     79  * <h5>Patterns and Their Interpretation</h5>
     80  * <p>
     81  * The pattern text defines the message output for each plural case of the
     82  * specified locale. Syntax:
     83  * <blockquote><pre>
     84  * pluralStyle = [offsetValue] (selector '{' message '}')+
     85  * offsetValue = "offset:" number
     86  * selector = explicitValue | keyword
     87  * explicitValue = '=' number  // adjacent, no white space in between
     88  * keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
     89  * message: see {@link MessageFormat}
     90  * </pre></blockquote>
     91  * Pattern_White_Space between syntax elements is ignored, except
     92  * between the {curly braces} and their sub-message,
     93  * and between the '=' and the number of an explicitValue.
     94  * <p>
     95  * There are 6 predefined case keywords in CLDR/ICU - 'zero', 'one', 'two', 'few', 'many' and
     96  * 'other'. You always have to define a message text for the default plural case
     97  * "<code>other</code>" which is contained in every rule set.
     98  * If you do not specify a message text for a particular plural case, the
     99  * message text of the plural case "<code>other</code>" gets assigned to this
    100  * plural case.
    101  * <p>
    102  * When formatting, the input number is first matched against the explicitValue clauses.
    103  * If there is no exact-number match, then a keyword is selected by calling
    104  * the <code>PluralRules</code> with the input number <em>minus the offset</em>.
    105  * (The offset defaults to 0 if it is omitted from the pattern string.)
    106  * If there is no clause with that keyword, then the "other" clauses is returned.
    107  * <p>
    108  * An unquoted pound sign (<code>#</code>) in the selected sub-message
    109  * itself (i.e., outside of arguments nested in the sub-message)
    110  * is replaced by the input number minus the offset.
    111  * The number-minus-offset value is formatted using a
    112  * <code>NumberFormat</code> for the <code>PluralFormat</code>'s locale. If you
    113  * need special number formatting, you have to use a <code>MessageFormat</code>
    114  * and explicitly specify a <code>NumberFormat</code> argument.
    115  * <strong>Note:</strong> That argument is formatting without subtracting the offset!
    116  * If you need a custom format and have a non-zero offset, then you need to pass the
    117  * number-minus-offset value as a separate parameter.
    118  *
    119  * <p>For a usage example, see the {@link MessageFormat} class documentation.
    120  *
    121  * <h4>Defining Custom Plural Rules</h4>
    122  * <p>If you need to use <code>PluralFormat</code> with custom rules, you can
    123  * create a <code>PluralRules</code> object and pass it to
    124  * <code>PluralFormat</code>'s constructor. If you also specify a locale in this
    125  * constructor, this locale will be used to format the number in the message
    126  * texts.
    127  * <p>
    128  * For more information about <code>PluralRules</code>, see
    129  * {@link PluralRules}.
    130  *
    131  * @author tschumann (Tim Schumann)
    132  */
    133 public class PluralFormat extends UFormat {
    134     private static final long serialVersionUID = 1L;
    135 
    136     /**
    137      * The locale used for standard number formatting and getting the predefined
    138      * plural rules (if they were not defined explicitely).
    139      * @serial
    140      */
    141     private ULocale ulocale = null;
    142 
    143     /**
    144      * The plural rules used for plural selection.
    145      * @serial
    146      */
    147     private PluralRules pluralRules = null;
    148 
    149     /**
    150      * The applied pattern string.
    151      * @serial
    152      */
    153     private String pattern = null;
    154 
    155     /**
    156      * The MessagePattern which contains the parsed structure of the pattern string.
    157      */
    158     transient private MessagePattern msgPattern;
    159 
    160     /**
    161      * Obsolete with use of MessagePattern since ICU 4.8. Used to be:
    162      * The format messages for each plural case. It is a mapping:
    163      *  <code>String</code>(plural case keyword) --&gt; <code>String</code>
    164      *  (message for this plural case).
    165      * @serial
    166      */
    167     private Map<String, String> parsedValues = null;
    168 
    169     /**
    170      * This <code>NumberFormat</code> is used for the standard formatting of
    171      * the number inserted into the message.
    172      * @serial
    173      */
    174     private NumberFormat numberFormat = null;
    175 
    176     /**
    177      * The offset to subtract before invoking plural rules.
    178      */
    179     transient private double offset = 0;
    180 
    181     /**
    182      * Creates a new cardinal-number <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
    183      * This locale will be used to get the set of plural rules and for standard
    184      * number formatting.
    185      * @see Category#FORMAT
    186      */
    187     public PluralFormat() {
    188         init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
    189     }
    190 
    191     /**
    192      * Creates a new cardinal-number <code>PluralFormat</code> for a given locale.
    193      * @param ulocale the <code>PluralFormat</code> will be configured with
    194      *        rules for this locale. This locale will also be used for standard
    195      *        number formatting.
    196      */
    197     public PluralFormat(ULocale ulocale) {
    198         init(null, PluralType.CARDINAL, ulocale, null);
    199     }
    200 
    201     /**
    202      * Creates a new cardinal-number <code>PluralFormat</code> for a given
    203      * {@link java.util.Locale}.
    204      * @param locale the <code>PluralFormat</code> will be configured with
    205      *        rules for this locale. This locale will also be used for standard
    206      *        number formatting.
    207      */
    208     public PluralFormat(Locale locale) {
    209         this(ULocale.forLocale(locale));
    210     }
    211 
    212     /**
    213      * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
    214      * The standard number formatting will be done using the default <code>FORMAT</code> locale.
    215      * @param rules defines the behavior of the <code>PluralFormat</code>
    216      *        object.
    217      * @see Category#FORMAT
    218      */
    219     public PluralFormat(PluralRules rules) {
    220         init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
    221     }
    222 
    223     /**
    224      * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
    225      * The standard number formatting will be done using the given locale.
    226      * @param ulocale the default number formatting will be done using this
    227      *        locale.
    228      * @param rules defines the behavior of the <code>PluralFormat</code>
    229      *        object.
    230      */
    231     public PluralFormat(ULocale ulocale, PluralRules rules) {
    232         init(rules, PluralType.CARDINAL, ulocale, null);
    233     }
    234 
    235     /**
    236      * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules.
    237      * The standard number formatting will be done using the given locale.
    238      * @param locale the default number formatting will be done using this
    239      *        locale.
    240      * @param rules defines the behavior of the <code>PluralFormat</code>
    241      *        object.
    242      */
    243     public PluralFormat(Locale locale, PluralRules rules) {
    244         this(ULocale.forLocale(locale), rules);
    245     }
    246 
    247     /**
    248      * Creates a new <code>PluralFormat</code> for the plural type.
    249      * The standard number formatting will be done using the given locale.
    250      * @param ulocale the default number formatting will be done using this
    251      *        locale.
    252      * @param type The plural type (e.g., cardinal or ordinal).
    253      */
    254     public PluralFormat(ULocale ulocale, PluralType type) {
    255         init(null, type, ulocale, null);
    256     }
    257 
    258     /**
    259      * Creates a new <code>PluralFormat</code> for the plural type.
    260      * The standard number formatting will be done using the given {@link java.util.Locale}.
    261      * @param locale the default number formatting will be done using this
    262      *        locale.
    263      * @param type The plural type (e.g., cardinal or ordinal).
    264      */
    265     public PluralFormat(Locale locale, PluralType type) {
    266         this(ULocale.forLocale(locale), type);
    267     }
    268 
    269     /**
    270      * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string.
    271      * The default <code>FORMAT</code> locale will be used to get the set of plural rules and for
    272      * standard number formatting.
    273      * @param  pattern the pattern for this <code>PluralFormat</code>.
    274      * @throws IllegalArgumentException if the pattern is invalid.
    275      * @see Category#FORMAT
    276      */
    277     public PluralFormat(String pattern) {
    278         init(null, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
    279         applyPattern(pattern);
    280     }
    281 
    282     /**
    283      * Creates a new cardinal-number <code>PluralFormat</code> for a given pattern string and
    284      * locale.
    285      * The locale will be used to get the set of plural rules and for
    286      * standard number formatting.
    287      * <p>Example code:{@sample external/icu/android_icu4j/src/samples/java/android/icu/samples/text/pluralformat/PluralFormatSample.java PluralFormatExample}
    288      * @param ulocale the <code>PluralFormat</code> will be configured with
    289      *        rules for this locale. This locale will also be used for standard
    290      *        number formatting.
    291      * @param  pattern the pattern for this <code>PluralFormat</code>.
    292      * @throws IllegalArgumentException if the pattern is invalid.
    293      */
    294     public PluralFormat(ULocale ulocale, String pattern) {
    295         init(null, PluralType.CARDINAL, ulocale, null);
    296         applyPattern(pattern);
    297     }
    298 
    299     /**
    300      * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules and a
    301      * pattern.
    302      * The standard number formatting will be done using the default <code>FORMAT</code> locale.
    303      * @param rules defines the behavior of the <code>PluralFormat</code>
    304      *        object.
    305      * @param  pattern the pattern for this <code>PluralFormat</code>.
    306      * @throws IllegalArgumentException if the pattern is invalid.
    307      * @see Category#FORMAT
    308      */
    309     public PluralFormat(PluralRules rules, String pattern) {
    310         init(rules, PluralType.CARDINAL, ULocale.getDefault(Category.FORMAT), null);
    311         applyPattern(pattern);
    312     }
    313 
    314     /**
    315      * Creates a new cardinal-number <code>PluralFormat</code> for a given set of rules, a
    316      * pattern and a locale.
    317      * @param ulocale the <code>PluralFormat</code> will be configured with
    318      *        rules for this locale. This locale will also be used for standard
    319      *        number formatting.
    320      * @param rules defines the behavior of the <code>PluralFormat</code>
    321      *        object.
    322      * @param  pattern the pattern for this <code>PluralFormat</code>.
    323      * @throws IllegalArgumentException if the pattern is invalid.
    324      */
    325     public PluralFormat(ULocale ulocale, PluralRules rules, String pattern) {
    326         init(rules, PluralType.CARDINAL, ulocale, null);
    327         applyPattern(pattern);
    328     }
    329 
    330     /**
    331      * Creates a new <code>PluralFormat</code> for a plural type, a
    332      * pattern and a locale.
    333      * @param ulocale the <code>PluralFormat</code> will be configured with
    334      *        rules for this locale. This locale will also be used for standard
    335      *        number formatting.
    336      * @param type The plural type (e.g., cardinal or ordinal).
    337      * @param  pattern the pattern for this <code>PluralFormat</code>.
    338      * @throws IllegalArgumentException if the pattern is invalid.
    339      */
    340     public PluralFormat(ULocale ulocale, PluralType type, String pattern) {
    341         init(null, type, ulocale, null);
    342         applyPattern(pattern);
    343     }
    344 
    345     /**
    346      * Creates a new <code>PluralFormat</code> for a plural type, a
    347      * pattern and a locale.
    348      * @param ulocale the <code>PluralFormat</code> will be configured with
    349      *        rules for this locale. This locale will also be used for standard
    350      *        number formatting.
    351      * @param type The plural type (e.g., cardinal or ordinal).
    352      * @param pattern the pattern for this <code>PluralFormat</code>.
    353      * @param numberFormat The number formatter to use.
    354      * @throws IllegalArgumentException if the pattern is invalid.
    355      */
    356     /*package*/ PluralFormat(ULocale ulocale, PluralType type, String pattern, NumberFormat numberFormat) {
    357         init(null, type, ulocale, numberFormat);
    358         applyPattern(pattern);
    359     }
    360 
    361     /*
    362      * Initializes the <code>PluralRules</code> object.
    363      * Postcondition:<br/>
    364      *   <code>ulocale</code>    :  is <code>locale</code><br/>
    365      *   <code>pluralRules</code>:  if <code>rules</code> != <code>null</code>
    366      *                              it's set to rules, otherwise it is the
    367      *                              predefined plural rule set for the locale
    368      *                              <code>ulocale</code>.<br/>
    369      *   <code>parsedValues</code>: is <code>null</code><br/>
    370      *   <code>pattern</code>:      is <code>null</code><br/>
    371      *   <code>numberFormat</code>: a <code>NumberFormat</code> for the locale
    372      *                              <code>ulocale</code>.
    373      */
    374     private void init(PluralRules rules, PluralType type, ULocale locale, NumberFormat numberFormat) {
    375         ulocale = locale;
    376         pluralRules = (rules == null) ? PluralRules.forLocale(ulocale, type)
    377                                       : rules;
    378         resetPattern();
    379         this.numberFormat = (numberFormat == null) ? NumberFormat.getInstance(ulocale) : numberFormat;
    380     }
    381 
    382     private void resetPattern() {
    383         pattern = null;
    384         if(msgPattern != null) {
    385             msgPattern.clear();
    386         }
    387         offset = 0;
    388     }
    389 
    390     /**
    391      * Sets the pattern used by this plural format.
    392      * The method parses the pattern and creates a map of format strings
    393      * for the plural rules.
    394      * Patterns and their interpretation are specified in the class description.
    395      *
    396      * @param pattern the pattern for this plural format.
    397      * @throws IllegalArgumentException if the pattern is invalid.
    398      */
    399     public void applyPattern(String pattern) {
    400         this.pattern = pattern;
    401         if (msgPattern == null) {
    402             msgPattern = new MessagePattern();
    403         }
    404         try {
    405             msgPattern.parsePluralStyle(pattern);
    406             offset = msgPattern.getPluralOffset(0);
    407         } catch(RuntimeException e) {
    408             resetPattern();
    409             throw e;
    410         }
    411     }
    412 
    413     /**
    414      * Returns the pattern for this PluralFormat.
    415      *
    416      * @return the pattern string
    417      */
    418     public String toPattern() {
    419         return pattern;
    420     }
    421 
    422     /**
    423      * Finds the PluralFormat sub-message for the given number, or the "other" sub-message.
    424      * @param pattern A MessagePattern.
    425      * @param partIndex the index of the first PluralFormat argument style part.
    426      * @param selector the PluralSelector for mapping the number (minus offset) to a keyword.
    427      * @param context worker object for the selector.
    428      * @param number a number to be matched to one of the PluralFormat argument's explicit values,
    429      *        or mapped via the PluralSelector.
    430      * @return the sub-message start part index.
    431      */
    432     /*package*/ static int findSubMessage(
    433             MessagePattern pattern, int partIndex,
    434             PluralSelector selector, Object context, double number) {
    435         int count=pattern.countParts();
    436         double offset;
    437         MessagePattern.Part part=pattern.getPart(partIndex);
    438         if(part.getType().hasNumericValue()) {
    439             offset=pattern.getNumericValue(part);
    440             ++partIndex;
    441         } else {
    442             offset=0;
    443         }
    444         // The keyword is null until we need to match against a non-explicit, not-"other" value.
    445         // Then we get the keyword from the selector.
    446         // (In other words, we never call the selector if we match against an explicit value,
    447         // or if the only non-explicit keyword is "other".)
    448         String keyword=null;
    449         // When we find a match, we set msgStart>0 and also set this boolean to true
    450         // to avoid matching the keyword again (duplicates are allowed)
    451         // while we continue to look for an explicit-value match.
    452         boolean haveKeywordMatch=false;
    453         // msgStart is 0 until we find any appropriate sub-message.
    454         // We remember the first "other" sub-message if we have not seen any
    455         // appropriate sub-message before.
    456         // We remember the first matching-keyword sub-message if we have not seen
    457         // one of those before.
    458         // (The parser allows [does not check for] duplicate keywords.
    459         // We just have to make sure to take the first one.)
    460         // We avoid matching the keyword twice by also setting haveKeywordMatch=true
    461         // at the first keyword match.
    462         // We keep going until we find an explicit-value match or reach the end of the plural style.
    463         int msgStart=0;
    464         // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
    465         // until ARG_LIMIT or end of plural-only pattern.
    466         do {
    467             part=pattern.getPart(partIndex++);
    468             MessagePattern.Part.Type type=part.getType();
    469             if(type==MessagePattern.Part.Type.ARG_LIMIT) {
    470                 break;
    471             }
    472             assert type==MessagePattern.Part.Type.ARG_SELECTOR;
    473             // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
    474             if(pattern.getPartType(partIndex).hasNumericValue()) {
    475                 // explicit value like "=2"
    476                 part=pattern.getPart(partIndex++);
    477                 if(number==pattern.getNumericValue(part)) {
    478                     // matches explicit value
    479                     return partIndex;
    480                 }
    481             } else if(!haveKeywordMatch) {
    482                 // plural keyword like "few" or "other"
    483                 // Compare "other" first and call the selector if this is not "other".
    484                 if(pattern.partSubstringMatches(part, "other")) {
    485                     if(msgStart==0) {
    486                         msgStart=partIndex;
    487                         if(keyword!=null && keyword.equals("other")) {
    488                             // This is the first "other" sub-message,
    489                             // and the selected keyword is also "other".
    490                             // Do not match "other" again.
    491                             haveKeywordMatch=true;
    492                         }
    493                     }
    494                 } else {
    495                     if(keyword==null) {
    496                         keyword=selector.select(context, number-offset);
    497                         if(msgStart!=0 && keyword.equals("other")) {
    498                             // We have already seen an "other" sub-message.
    499                             // Do not match "other" again.
    500                             haveKeywordMatch=true;
    501                             // Skip keyword matching but do getLimitPartIndex().
    502                         }
    503                     }
    504                     if(!haveKeywordMatch && pattern.partSubstringMatches(part, keyword)) {
    505                         // keyword matches
    506                         msgStart=partIndex;
    507                         // Do not match this keyword again.
    508                         haveKeywordMatch=true;
    509                     }
    510                 }
    511             }
    512             partIndex=pattern.getLimitPartIndex(partIndex);
    513         } while(++partIndex<count);
    514         return msgStart;
    515     }
    516 
    517     /**
    518      * Interface for selecting PluralFormat keywords for numbers.
    519      * The PluralRules class was intended to implement this interface,
    520      * but there is no public API that uses a PluralSelector,
    521      * only MessageFormat and PluralFormat have PluralSelector implementations.
    522      * Therefore, PluralRules is not marked to implement this non-public interface,
    523      * to avoid confusing users.
    524      * @hide draft / provisional / internal are hidden on Android
    525      */
    526     /*package*/ interface PluralSelector {
    527         /**
    528          * Given a number, returns the appropriate PluralFormat keyword.
    529          *
    530          * @param context worker object for the selector.
    531          * @param number The number to be plural-formatted.
    532          * @return The selected PluralFormat keyword.
    533          */
    534         public String select(Object context, double number);
    535     }
    536 
    537     // See PluralSelector:
    538     // We could avoid this adapter class if we made PluralSelector public
    539     // (or at least publicly visible) and had PluralRules implement PluralSelector.
    540     private final class PluralSelectorAdapter implements PluralSelector {
    541         @Override
    542         public String select(Object context, double number) {
    543             IFixedDecimal dec = (IFixedDecimal) context;
    544             return pluralRules.select(dec);
    545         }
    546     }
    547     transient private PluralSelectorAdapter pluralRulesWrapper = new PluralSelectorAdapter();
    548 
    549     /**
    550      * Formats a plural message for a given number.
    551      *
    552      * @param number a number for which the plural message should be formatted.
    553      *        If no pattern has been applied to this
    554      *        <code>PluralFormat</code> object yet, the formatted number will
    555      *        be returned.
    556      * @return the string containing the formatted plural message.
    557      */
    558     public final String format(double number) {
    559         return format(number, number);
    560     }
    561 
    562     /**
    563      * Formats a plural message for a given number and appends the formatted
    564      * message to the given <code>StringBuffer</code>.
    565      * @param number a number object (instance of <code>Number</code> for which
    566      *        the plural message should be formatted. If no pattern has been
    567      *        applied to this <code>PluralFormat</code> object yet, the
    568      *        formatted number will be returned.
    569      *        Note: If this object is not an instance of <code>Number</code>,
    570      *              the <code>toAppendTo</code> will not be modified.
    571      * @param toAppendTo the formatted message will be appended to this
    572      *        <code>StringBuffer</code>.
    573      * @param pos will be ignored by this method.
    574      * @return the string buffer passed in as toAppendTo, with formatted text
    575      *         appended.
    576      * @throws IllegalArgumentException if number is not an instance of Number
    577      */
    578     @Override
    579     public StringBuffer format(Object number, StringBuffer toAppendTo,
    580             FieldPosition pos) {
    581         if (!(number instanceof Number)) {
    582             throw new IllegalArgumentException("'" + number + "' is not a Number");
    583         }
    584         Number numberObject = (Number) number;
    585         toAppendTo.append(format(numberObject, numberObject.doubleValue()));
    586         return toAppendTo;
    587     }
    588 
    589     private String format(Number numberObject, double number) {
    590         // If no pattern was applied, return the formatted number.
    591         if (msgPattern == null || msgPattern.countParts() == 0) {
    592             return numberFormat.format(numberObject);
    593         }
    594 
    595         // Get the appropriate sub-message.
    596         // Select it based on the formatted number-offset.
    597         double numberMinusOffset = number - offset;
    598         String numberString;
    599         if (offset == 0) {
    600             numberString = numberFormat.format(numberObject);  // could be BigDecimal etc.
    601         } else {
    602             numberString = numberFormat.format(numberMinusOffset);
    603         }
    604         IFixedDecimal dec;
    605         if(numberFormat instanceof DecimalFormat) {
    606             dec = ((DecimalFormat) numberFormat).getFixedDecimal(numberMinusOffset);
    607         } else {
    608             dec = new FixedDecimal(numberMinusOffset);
    609         }
    610         int partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, dec, number);
    611         // Replace syntactic # signs in the top level of this sub-message
    612         // (not in nested arguments) with the formatted number-offset.
    613         StringBuilder result = null;
    614         int prevIndex = msgPattern.getPart(partIndex).getLimit();
    615         for (;;) {
    616             MessagePattern.Part part = msgPattern.getPart(++partIndex);
    617             MessagePattern.Part.Type type = part.getType();
    618             int index = part.getIndex();
    619             if (type == MessagePattern.Part.Type.MSG_LIMIT) {
    620                 if (result == null) {
    621                     return pattern.substring(prevIndex, index);
    622                 } else {
    623                     return result.append(pattern, prevIndex, index).toString();
    624                 }
    625             } else if (type == MessagePattern.Part.Type.REPLACE_NUMBER ||
    626                         // JDK compatibility mode: Remove SKIP_SYNTAX.
    627                         (type == MessagePattern.Part.Type.SKIP_SYNTAX && msgPattern.jdkAposMode())) {
    628                 if (result == null) {
    629                     result = new StringBuilder();
    630                 }
    631                 result.append(pattern, prevIndex, index);
    632                 if (type == MessagePattern.Part.Type.REPLACE_NUMBER) {
    633                     result.append(numberString);
    634                 }
    635                 prevIndex = part.getLimit();
    636             } else if (type == MessagePattern.Part.Type.ARG_START) {
    637                 if (result == null) {
    638                     result = new StringBuilder();
    639                 }
    640                 result.append(pattern, prevIndex, index);
    641                 prevIndex = index;
    642                 partIndex = msgPattern.getLimitPartIndex(partIndex);
    643                 index = msgPattern.getPart(partIndex).getLimit();
    644                 MessagePattern.appendReducedApostrophes(pattern, prevIndex, index, result);
    645                 prevIndex = index;
    646             }
    647         }
    648     }
    649 
    650     /**
    651      * This method is not yet supported by <code>PluralFormat</code>.
    652      * @param text the string to be parsed.
    653      * @param parsePosition defines the position where parsing is to begin,
    654      * and upon return, the position where parsing left off.  If the position
    655      * has not changed upon return, then parsing failed.
    656      * @return nothing because this method is not yet implemented.
    657      * @throws UnsupportedOperationException will always be thrown by this method.
    658      */
    659     public Number parse(String text, ParsePosition parsePosition) {
    660         // You get number ranges from this. You can't get an exact number.
    661         throw new UnsupportedOperationException();
    662     }
    663 
    664     /**
    665      * This method is not yet supported by <code>PluralFormat</code>.
    666      * @param source the string to be parsed.
    667      * @param pos defines the position where parsing is to begin,
    668      * and upon return, the position where parsing left off.  If the position
    669      * has not changed upon return, then parsing failed.
    670      * @return nothing because this method is not yet implemented.
    671      * @throws UnsupportedOperationException will always be thrown by this method.
    672      */
    673     @Override
    674     public Object parseObject(String source, ParsePosition pos) {
    675         throw new UnsupportedOperationException();
    676     }
    677 
    678     /**
    679      * This method returns the PluralRules type found from parsing.
    680      * @param source the string to be parsed.
    681      * @param pos defines the position where parsing is to begin,
    682      * and upon return, the position where parsing left off.  If the position
    683      * is a negative index, then parsing failed.
    684      * @return Returns the PluralRules type. For example, it could be "zero", "one", "two", "few", "many" or "other")
    685      */
    686     /*package*/ String parseType(String source, RbnfLenientScanner scanner, FieldPosition pos) {
    687         // If no pattern was applied, return null.
    688         if (msgPattern == null || msgPattern.countParts() == 0) {
    689             pos.setBeginIndex(-1);
    690             pos.setEndIndex(-1);
    691             return null;
    692         }
    693         int partIndex = 0;
    694         int currMatchIndex;
    695         int count=msgPattern.countParts();
    696         int startingAt = pos.getBeginIndex();
    697         if (startingAt < 0) {
    698             startingAt = 0;
    699         }
    700 
    701         // The keyword is null until we need to match against a non-explicit, not-"other" value.
    702         // Then we get the keyword from the selector.
    703         // (In other words, we never call the selector if we match against an explicit value,
    704         // or if the only non-explicit keyword is "other".)
    705         String keyword = null;
    706         String matchedWord = null;
    707         int matchedIndex = -1;
    708         // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples
    709         // until the end of the plural-only pattern.
    710         while (partIndex < count) {
    711             MessagePattern.Part partSelector=msgPattern.getPart(partIndex++);
    712             if (partSelector.getType() != MessagePattern.Part.Type.ARG_SELECTOR) {
    713                 // Bad format
    714                 continue;
    715             }
    716 
    717             MessagePattern.Part partStart=msgPattern.getPart(partIndex++);
    718             if (partStart.getType() != MessagePattern.Part.Type.MSG_START) {
    719                 // Bad format
    720                 continue;
    721             }
    722 
    723             MessagePattern.Part partLimit=msgPattern.getPart(partIndex++);
    724             if (partLimit.getType() != MessagePattern.Part.Type.MSG_LIMIT) {
    725                 // Bad format
    726                 continue;
    727             }
    728 
    729             String currArg = pattern.substring(partStart.getLimit(), partLimit.getIndex());
    730             if (scanner != null) {
    731                 // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us.
    732                 int[] scannerMatchResult = scanner.findText(source, currArg, startingAt);
    733                 currMatchIndex = scannerMatchResult[0];
    734             }
    735             else {
    736                 currMatchIndex = source.indexOf(currArg, startingAt);
    737             }
    738             if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && (matchedWord == null || currArg.length() > matchedWord.length())) {
    739                 matchedIndex = currMatchIndex;
    740                 matchedWord = currArg;
    741                 keyword = pattern.substring(partStart.getLimit(), partLimit.getIndex());
    742             }
    743         }
    744         if (keyword != null) {
    745             pos.setBeginIndex(matchedIndex);
    746             pos.setEndIndex(matchedIndex + matchedWord.length());
    747             return keyword;
    748         }
    749 
    750         // Not found!
    751         pos.setBeginIndex(-1);
    752         pos.setEndIndex(-1);
    753         return null;
    754     }
    755 
    756     /**
    757      * Sets the locale used by this <code>PluraFormat</code> object.
    758      * Note: Calling this method resets this <code>PluraFormat</code> object,
    759      *     i.e., a pattern that was applied previously will be removed,
    760      *     and the NumberFormat is set to the default number format for
    761      *     the locale.  The resulting format behaves the same as one
    762      *     constructed from {@link #PluralFormat(ULocale, PluralRules.PluralType)}
    763      *     with PluralType.CARDINAL.
    764      * @param ulocale the <code>ULocale</code> used to configure the
    765      *     formatter. If <code>ulocale</code> is <code>null</code>, the
    766      *     default <code>FORMAT</code> locale will be used.
    767      * @see Category#FORMAT
    768      * @deprecated ICU 50 This method clears the pattern and might create
    769      *             a different kind of PluralRules instance;
    770      *             use one of the constructors to create a new instance instead.
    771      * @hide original deprecated declaration
    772      */
    773     @Deprecated
    774     public void setLocale(ULocale ulocale) {
    775         if (ulocale == null) {
    776             ulocale = ULocale.getDefault(Category.FORMAT);
    777         }
    778         init(null, PluralType.CARDINAL, ulocale, null);
    779     }
    780 
    781     /**
    782      * Sets the number format used by this formatter.  You only need to
    783      * call this if you want a different number format than the default
    784      * formatter for the locale.
    785      * @param format the number format to use.
    786      */
    787     public void setNumberFormat(NumberFormat format) {
    788         numberFormat = format;
    789     }
    790 
    791     /**
    792      * {@inheritDoc}
    793      */
    794     @Override
    795     public boolean equals(Object rhs) {
    796         if(this == rhs) {
    797             return true;
    798         }
    799         if(rhs == null || getClass() != rhs.getClass()) {
    800             return false;
    801         }
    802         PluralFormat pf = (PluralFormat)rhs;
    803         return
    804             Utility.objectEquals(ulocale, pf.ulocale) &&
    805             Utility.objectEquals(pluralRules, pf.pluralRules) &&
    806             Utility.objectEquals(msgPattern, pf.msgPattern) &&
    807             Utility.objectEquals(numberFormat, pf.numberFormat);
    808     }
    809 
    810     /**
    811      * Returns true if this equals the provided PluralFormat.
    812      * @param rhs the PluralFormat to compare against
    813      * @return true if this equals rhs
    814      */
    815     public boolean equals(PluralFormat rhs) {
    816         return equals((Object)rhs);
    817     }
    818 
    819     /**
    820      * {@inheritDoc}
    821      */
    822     @Override
    823     public int hashCode() {
    824         return pluralRules.hashCode() ^ parsedValues.hashCode();
    825     }
    826 
    827     /**
    828      * {@inheritDoc}
    829      */
    830     @Override
    831     public String toString() {
    832         StringBuilder buf = new StringBuilder();
    833         buf.append("locale=" + ulocale);
    834         buf.append(", rules='" + pluralRules + "'");
    835         buf.append(", pattern='" + pattern + "'");
    836         buf.append(", format='" + numberFormat + "'");
    837         return buf.toString();
    838     }
    839 
    840     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    841         in.defaultReadObject();
    842         pluralRulesWrapper = new PluralSelectorAdapter();
    843         // Ignore the parsedValues from an earlier class version (before ICU 4.8)
    844         // and rebuild the msgPattern.
    845         parsedValues = null;
    846         if (pattern != null) {
    847             applyPattern(pattern);
    848         }
    849     }
    850 }
    851