Home | History | Annotate | Download | only in text
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4 **********************************************************************
      5 * Copyright (c) 2004-2016, International Business Machines
      6 * Corporation and others.  All Rights Reserved.
      7 **********************************************************************
      8 * Author: Alan Liu
      9 * Created: April 6, 2004
     10 * Since: ICU 3.0
     11 **********************************************************************
     12 */
     13 package com.ibm.icu.text;
     14 
     15 import java.io.IOException;
     16 import java.io.InvalidObjectException;
     17 import java.io.ObjectInputStream;
     18 import java.text.AttributedCharacterIterator;
     19 import java.text.AttributedCharacterIterator.Attribute;
     20 import java.text.AttributedString;
     21 import java.text.CharacterIterator;
     22 import java.text.ChoiceFormat;
     23 import java.text.FieldPosition;
     24 import java.text.Format;
     25 import java.text.ParseException;
     26 import java.text.ParsePosition;
     27 import java.util.ArrayList;
     28 import java.util.Date;
     29 import java.util.HashMap;
     30 import java.util.HashSet;
     31 import java.util.Iterator;
     32 import java.util.List;
     33 import java.util.Locale;
     34 import java.util.Map;
     35 import java.util.Set;
     36 
     37 import com.ibm.icu.impl.PatternProps;
     38 import com.ibm.icu.impl.Utility;
     39 import com.ibm.icu.text.MessagePattern.ArgType;
     40 import com.ibm.icu.text.MessagePattern.Part;
     41 import com.ibm.icu.text.PluralRules.FixedDecimal;
     42 import com.ibm.icu.text.PluralRules.PluralType;
     43 import com.ibm.icu.util.ICUUncheckedIOException;
     44 import com.ibm.icu.util.ULocale;
     45 import com.ibm.icu.util.ULocale.Category;
     46 
     47 /**
     48  * {@icuenhanced java.text.MessageFormat}.{@icu _usage_}
     49  *
     50  * <p>MessageFormat prepares strings for display to users,
     51  * with optional arguments (variables/placeholders).
     52  * The arguments can occur in any order, which is necessary for translation
     53  * into languages with different grammars.
     54  *
     55  * <p>A MessageFormat is constructed from a <em>pattern</em> string
     56  * with arguments in {curly braces} which will be replaced by formatted values.
     57  *
     58  * <p><code>MessageFormat</code> differs from the other <code>Format</code>
     59  * classes in that you create a <code>MessageFormat</code> object with one
     60  * of its constructors (not with a <code>getInstance</code> style factory
     61  * method). Factory methods aren't necessary because <code>MessageFormat</code>
     62  * itself doesn't implement locale-specific behavior. Any locale-specific
     63  * behavior is defined by the pattern that you provide and the
     64  * subformats used for inserted arguments.
     65  *
     66  * <p>Arguments can be named (using identifiers) or numbered (using small ASCII-digit integers).
     67  * Some of the API methods work only with argument numbers and throw an exception
     68  * if the pattern has named arguments (see {@link #usesNamedArguments()}).
     69  *
     70  * <p>An argument might not specify any format type. In this case,
     71  * a Number value is formatted with a default (for the locale) NumberFormat,
     72  * a Date value is formatted with a default (for the locale) DateFormat,
     73  * and for any other value its toString() value is used.
     74  *
     75  * <p>An argument might specify a "simple" type for which the specified
     76  * Format object is created, cached and used.
     77  *
     78  * <p>An argument might have a "complex" type with nested MessageFormat sub-patterns.
     79  * During formatting, one of these sub-messages is selected according to the argument value
     80  * and recursively formatted.
     81  *
     82  * <p>After construction, a custom Format object can be set for
     83  * a top-level argument, overriding the default formatting and parsing behavior
     84  * for that argument.
     85  * However, custom formatting can be achieved more simply by writing
     86  * a typeless argument in the pattern string
     87  * and supplying it with a preformatted string value.
     88  *
     89  * <p>When formatting, MessageFormat takes a collection of argument values
     90  * and writes an output string.
     91  * The argument values may be passed as an array
     92  * (when the pattern contains only numbered arguments)
     93  * or as a Map (which works for both named and numbered arguments).
     94  *
     95  * <p>Each argument is matched with one of the input values by array index or map key
     96  * and formatted according to its pattern specification
     97  * (or using a custom Format object if one was set).
     98  * A numbered pattern argument is matched with a map key that contains that number
     99  * as an ASCII-decimal-digit string (without leading zero).
    100  *
    101  * <h3><a name="patterns">Patterns and Their Interpretation</a></h3>
    102  *
    103  * <code>MessageFormat</code> uses patterns of the following form:
    104  * <blockquote><pre>
    105  * message = messageText (argument messageText)*
    106  * argument = noneArg | simpleArg | complexArg
    107  * complexArg = choiceArg | pluralArg | selectArg | selectordinalArg
    108  *
    109  * noneArg = '{' argNameOrNumber '}'
    110  * simpleArg = '{' argNameOrNumber ',' argType [',' argStyle] '}'
    111  * choiceArg = '{' argNameOrNumber ',' "choice" ',' choiceStyle '}'
    112  * pluralArg = '{' argNameOrNumber ',' "plural" ',' pluralStyle '}'
    113  * selectArg = '{' argNameOrNumber ',' "select" ',' selectStyle '}'
    114  * selectordinalArg = '{' argNameOrNumber ',' "selectordinal" ',' pluralStyle '}'
    115  *
    116  * choiceStyle: see {@link ChoiceFormat}
    117  * pluralStyle: see {@link PluralFormat}
    118  * selectStyle: see {@link SelectFormat}
    119  *
    120  * argNameOrNumber = argName | argNumber
    121  * argName = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
    122  * argNumber = '0' | ('1'..'9' ('0'..'9')*)
    123  *
    124  * argType = "number" | "date" | "time" | "spellout" | "ordinal" | "duration"
    125  * argStyle = "short" | "medium" | "long" | "full" | "integer" | "currency" | "percent" | argStyleText
    126  * </pre></blockquote>
    127  *
    128  * <ul>
    129  *   <li>messageText can contain quoted literal strings including syntax characters.
    130  *       A quoted literal string begins with an ASCII apostrophe and a syntax character
    131  *       (usually a {curly brace}) and continues until the next single apostrophe.
    132  *       A double ASCII apostrohpe inside or outside of a quoted string represents
    133  *       one literal apostrophe.
    134  *   <li>Quotable syntax characters are the {curly braces} in all messageText parts,
    135  *       plus the '#' sign in a messageText immediately inside a pluralStyle,
    136  *       and the '|' symbol in a messageText immediately inside a choiceStyle.
    137  *   <li>See also {@link MessagePattern.ApostropheMode}
    138  *   <li>In argStyleText, every single ASCII apostrophe begins and ends quoted literal text,
    139  *       and unquoted {curly braces} must occur in matched pairs.
    140  * </ul>
    141  *
    142  * <p>Recommendation: Use the real apostrophe (single quote) character \u2019 for
    143  * human-readable text, and use the ASCII apostrophe (\u0027 ' )
    144  * only in program syntax, like quoting in MessageFormat.
    145  * See the annotations for U+0027 Apostrophe in The Unicode Standard.
    146  *
    147  * <p>The <code>choice</code> argument type is deprecated.
    148  * Use <code>plural</code> arguments for proper plural selection,
    149  * and <code>select</code> arguments for simple selection among a fixed set of choices.
    150  *
    151  * <p>The <code>argType</code> and <code>argStyle</code> values are used to create
    152  * a <code>Format</code> instance for the format element. The following
    153  * table shows how the values map to Format instances. Combinations not
    154  * shown in the table are illegal. Any <code>argStyleText</code> must
    155  * be a valid pattern string for the Format subclass used.
    156  *
    157  * <table border=1>
    158  *    <tr>
    159  *       <th>argType
    160  *       <th>argStyle
    161  *       <th>resulting Format object
    162  *    <tr>
    163  *       <td colspan=2><i>(none)</i>
    164  *       <td><code>null</code>
    165  *    <tr>
    166  *       <td rowspan=5><code>number</code>
    167  *       <td><i>(none)</i>
    168  *       <td><code>NumberFormat.getInstance(getLocale())</code>
    169  *    <tr>
    170  *       <td><code>integer</code>
    171  *       <td><code>NumberFormat.getIntegerInstance(getLocale())</code>
    172  *    <tr>
    173  *       <td><code>currency</code>
    174  *       <td><code>NumberFormat.getCurrencyInstance(getLocale())</code>
    175  *    <tr>
    176  *       <td><code>percent</code>
    177  *       <td><code>NumberFormat.getPercentInstance(getLocale())</code>
    178  *    <tr>
    179  *       <td><i>argStyleText</i>
    180  *       <td><code>new DecimalFormat(argStyleText, new DecimalFormatSymbols(getLocale()))</code>
    181  *    <tr>
    182  *       <td rowspan=6><code>date</code>
    183  *       <td><i>(none)</i>
    184  *       <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
    185  *    <tr>
    186  *       <td><code>short</code>
    187  *       <td><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code>
    188  *    <tr>
    189  *       <td><code>medium</code>
    190  *       <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
    191  *    <tr>
    192  *       <td><code>long</code>
    193  *       <td><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code>
    194  *    <tr>
    195  *       <td><code>full</code>
    196  *       <td><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code>
    197  *    <tr>
    198  *       <td><i>argStyleText</i>
    199  *       <td><code>new SimpleDateFormat(argStyleText, getLocale())</code>
    200  *    <tr>
    201  *       <td rowspan=6><code>time</code>
    202  *       <td><i>(none)</i>
    203  *       <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
    204  *    <tr>
    205  *       <td><code>short</code>
    206  *       <td><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code>
    207  *    <tr>
    208  *       <td><code>medium</code>
    209  *       <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
    210  *    <tr>
    211  *       <td><code>long</code>
    212  *       <td><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code>
    213  *    <tr>
    214  *       <td><code>full</code>
    215  *       <td><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code>
    216  *    <tr>
    217  *       <td><i>argStyleText</i>
    218  *       <td><code>new SimpleDateFormat(argStyleText, getLocale())</code>
    219  *    <tr>
    220  *       <td><code>spellout</code>
    221  *       <td><i>argStyleText (optional)</i>
    222  *       <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.SPELLOUT)
    223  *           <br>&nbsp;&nbsp;&nbsp;&nbsp;.setDefaultRuleset(argStyleText);</code>
    224  *    <tr>
    225  *       <td><code>ordinal</code>
    226  *       <td><i>argStyleText (optional)</i>
    227  *       <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.ORDINAL)
    228  *           <br>&nbsp;&nbsp;&nbsp;&nbsp;.setDefaultRuleset(argStyleText);</code>
    229  *    <tr>
    230  *       <td><code>duration</code>
    231  *       <td><i>argStyleText (optional)</i>
    232  *       <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.DURATION)
    233  *           <br>&nbsp;&nbsp;&nbsp;&nbsp;.setDefaultRuleset(argStyleText);</code>
    234  * </table>
    235  *
    236  * <h4><a name="diffsjdk">Differences from java.text.MessageFormat</a></h4>
    237  *
    238  * <p>The ICU MessageFormat supports both named and numbered arguments,
    239  * while the JDK MessageFormat only supports numbered arguments.
    240  * Named arguments make patterns more readable.
    241  *
    242  * <p>ICU implements a more user-friendly apostrophe quoting syntax.
    243  * In message text, an apostrophe only begins quoting literal text
    244  * if it immediately precedes a syntax character (mostly {curly braces}).<br>
    245  * In the JDK MessageFormat, an apostrophe always begins quoting,
    246  * which requires common text like "don't" and "aujourd'hui"
    247  * to be written with doubled apostrophes like "don''t" and "aujourd''hui".
    248  * For more details see {@link MessagePattern.ApostropheMode}.
    249  *
    250  * <p>ICU does not create a ChoiceFormat object for a choiceArg, pluralArg or selectArg
    251  * but rather handles such arguments itself.
    252  * The JDK MessageFormat does create and use a ChoiceFormat object
    253  * (<code>new ChoiceFormat(argStyleText)</code>).
    254  * The JDK does not support plural and select arguments at all.
    255  *
    256  * <h4>Usage Information</h4>
    257  *
    258  * <p>Here are some examples of usage:
    259  * <blockquote>
    260  * <pre>
    261  * Object[] arguments = {
    262  *     7,
    263  *     new Date(System.currentTimeMillis()),
    264  *     "a disturbance in the Force"
    265  * };
    266  *
    267  * String result = MessageFormat.format(
    268  *     "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
    269  *     arguments);
    270  *
    271  * <em>output</em>: At 12:30 PM on Jul 3, 2053, there was a disturbance
    272  *           in the Force on planet 7.
    273  *
    274  * </pre>
    275  * </blockquote>
    276  * Typically, the message format will come from resources, and the
    277  * arguments will be dynamically set at runtime.
    278  *
    279  * <p>Example 2:
    280  * <blockquote>
    281  * <pre>
    282  * Object[] testArgs = { 3, "MyDisk" };
    283  *
    284  * MessageFormat form = new MessageFormat(
    285  *     "The disk \"{1}\" contains {0} file(s).");
    286  *
    287  * System.out.println(form.format(testArgs));
    288  *
    289  * // output, with different testArgs
    290  * <em>output</em>: The disk "MyDisk" contains 0 file(s).
    291  * <em>output</em>: The disk "MyDisk" contains 1 file(s).
    292  * <em>output</em>: The disk "MyDisk" contains 1,273 file(s).
    293  * </pre>
    294  * </blockquote>
    295  *
    296  * <p>For messages that include plural forms, you can use a plural argument:
    297  * <pre>
    298  * MessageFormat msgFmt = new MessageFormat(
    299  *     "{num_files, plural, " +
    300  *     "=0{There are no files on disk \"{disk_name}\".}" +
    301  *     "=1{There is one file on disk \"{disk_name}\".}" +
    302  *     "other{There are # files on disk \"{disk_name}\".}}",
    303  *     ULocale.ENGLISH);
    304  * Map args = new HashMap();
    305  * args.put("num_files", 0);
    306  * args.put("disk_name", "MyDisk");
    307  * System.out.println(msgFmt.format(args));
    308  * args.put("num_files", 3);
    309  * System.out.println(msgFmt.format(args));
    310  *
    311  * <em>output</em>:
    312  * There are no files on disk "MyDisk".
    313  * There are 3 files on "MyDisk".
    314  * </pre>
    315  * See {@link PluralFormat} and {@link PluralRules} for details.
    316  *
    317  * <h4><a name="synchronization">Synchronization</a></h4>
    318  *
    319  * <p>MessageFormats are not synchronized.
    320  * It is recommended to create separate format instances for each thread.
    321  * If multiple threads access a format concurrently, it must be synchronized
    322  * externally.
    323  *
    324  * @see          java.util.Locale
    325  * @see          Format
    326  * @see          NumberFormat
    327  * @see          DecimalFormat
    328  * @see          ChoiceFormat
    329  * @see          PluralFormat
    330  * @see          SelectFormat
    331  * @author       Mark Davis
    332  * @author       Markus Scherer
    333  * @stable ICU 3.0
    334  */
    335 public class MessageFormat extends UFormat {
    336 
    337     // Incremented by 1 for ICU 4.8's new format.
    338     static final long serialVersionUID = 7136212545847378652L;
    339 
    340     /**
    341      * Constructs a MessageFormat for the default <code>FORMAT</code> locale and the
    342      * specified pattern.
    343      * Sets the locale and calls applyPattern(pattern).
    344      *
    345      * @param pattern the pattern for this message format
    346      * @exception IllegalArgumentException if the pattern is invalid
    347      * @see Category#FORMAT
    348      * @stable ICU 3.0
    349      */
    350     public MessageFormat(String pattern) {
    351         this.ulocale = ULocale.getDefault(Category.FORMAT);
    352         applyPattern(pattern);
    353     }
    354 
    355     /**
    356      * Constructs a MessageFormat for the specified locale and
    357      * pattern.
    358      * Sets the locale and calls applyPattern(pattern).
    359      *
    360      * @param pattern the pattern for this message format
    361      * @param locale the locale for this message format
    362      * @exception IllegalArgumentException if the pattern is invalid
    363      * @stable ICU 3.0
    364      */
    365     public MessageFormat(String pattern, Locale locale) {
    366         this(pattern, ULocale.forLocale(locale));
    367     }
    368 
    369     /**
    370      * Constructs a MessageFormat for the specified locale and
    371      * pattern.
    372      * Sets the locale and calls applyPattern(pattern).
    373      *
    374      * @param pattern the pattern for this message format
    375      * @param locale the locale for this message format
    376      * @exception IllegalArgumentException if the pattern is invalid
    377      * @stable ICU 3.2
    378      */
    379     public MessageFormat(String pattern, ULocale locale) {
    380         this.ulocale = locale;
    381         applyPattern(pattern);
    382     }
    383 
    384     /**
    385      * Sets the locale to be used for creating argument Format objects.
    386      * This affects subsequent calls to the {@link #applyPattern applyPattern}
    387      * method as well as to the <code>format</code> and
    388      * {@link #formatToCharacterIterator formatToCharacterIterator} methods.
    389      *
    390      * @param locale the locale to be used when creating or comparing subformats
    391      * @stable ICU 3.0
    392      */
    393     public void setLocale(Locale locale) {
    394         setLocale(ULocale.forLocale(locale));
    395     }
    396 
    397     /**
    398      * Sets the locale to be used for creating argument Format objects.
    399      * This affects subsequent calls to the {@link #applyPattern applyPattern}
    400      * method as well as to the <code>format</code> and
    401      * {@link #formatToCharacterIterator formatToCharacterIterator} methods.
    402      *
    403      * @param locale the locale to be used when creating or comparing subformats
    404      * @stable ICU 3.2
    405      */
    406     public void setLocale(ULocale locale) {
    407         /* Save the pattern, and then reapply so that */
    408         /* we pick up any changes in locale specific */
    409         /* elements */
    410         String existingPattern = toPattern();                       /*ibm.3550*/
    411         this.ulocale = locale;
    412         // Invalidate all stock formatters. They are no longer valid since
    413         // the locale has changed.
    414         stockDateFormatter = null;
    415         stockNumberFormatter = null;
    416         pluralProvider = null;
    417         ordinalProvider = null;
    418         applyPattern(existingPattern);                              /*ibm.3550*/
    419     }
    420 
    421     /**
    422      * Returns the locale that's used when creating or comparing subformats.
    423      *
    424      * @return the locale used when creating or comparing subformats
    425      * @stable ICU 3.0
    426      */
    427     public Locale getLocale() {
    428         return ulocale.toLocale();
    429     }
    430 
    431     /**
    432      * {@icu} Returns the locale that's used when creating argument Format objects.
    433      *
    434      * @return the locale used when creating or comparing subformats
    435      * @stable ICU 3.2
    436      */
    437     public ULocale getULocale() {
    438         return ulocale;
    439     }
    440 
    441     /**
    442      * Sets the pattern used by this message format.
    443      * Parses the pattern and caches Format objects for simple argument types.
    444      * Patterns and their interpretation are specified in the
    445      * <a href="#patterns">class description</a>.
    446      *
    447      * @param pttrn the pattern for this message format
    448      * @throws IllegalArgumentException if the pattern is invalid
    449      * @stable ICU 3.0
    450      */
    451     public void applyPattern(String pttrn) {
    452         try {
    453             if (msgPattern == null) {
    454                 msgPattern = new MessagePattern(pttrn);
    455             } else {
    456                 msgPattern.parse(pttrn);
    457             }
    458             // Cache the formats that are explicitly mentioned in the message pattern.
    459             cacheExplicitFormats();
    460         } catch(RuntimeException e) {
    461             resetPattern();
    462             throw e;
    463         }
    464     }
    465 
    466     /**
    467      * {@icu} Sets the ApostropheMode and the pattern used by this message format.
    468      * Parses the pattern and caches Format objects for simple argument types.
    469      * Patterns and their interpretation are specified in the
    470      * <a href="#patterns">class description</a>.
    471      * <p>
    472      * This method is best used only once on a given object to avoid confusion about the mode,
    473      * and after constructing the object with an empty pattern string to minimize overhead.
    474      *
    475      * @param pattern the pattern for this message format
    476      * @param aposMode the new ApostropheMode
    477      * @throws IllegalArgumentException if the pattern is invalid
    478      * @see MessagePattern.ApostropheMode
    479      * @stable ICU 4.8
    480      */
    481     public void applyPattern(String pattern, MessagePattern.ApostropheMode aposMode) {
    482         if (msgPattern == null) {
    483             msgPattern = new MessagePattern(aposMode);
    484         } else if (aposMode != msgPattern.getApostropheMode()) {
    485             msgPattern.clearPatternAndSetApostropheMode(aposMode);
    486         }
    487         applyPattern(pattern);
    488     }
    489 
    490     /**
    491      * {@icu}
    492      * @return this instance's ApostropheMode.
    493      * @stable ICU 4.8
    494      */
    495     public MessagePattern.ApostropheMode getApostropheMode() {
    496         if (msgPattern == null) {
    497             msgPattern = new MessagePattern();  // Sets the default mode.
    498         }
    499         return msgPattern.getApostropheMode();
    500     }
    501 
    502     /**
    503      * Returns the applied pattern string.
    504      * @return the pattern string
    505      * @throws IllegalStateException after custom Format objects have been set
    506      *         via setFormat() or similar APIs
    507      * @stable ICU 3.0
    508      */
    509     public String toPattern() {
    510         // Return the original, applied pattern string, or else "".
    511         // Note: This does not take into account
    512         // - changes from setFormat() and similar methods, or
    513         // - normalization of apostrophes and arguments, for example,
    514         //   whether some date/time/number formatter was created via a pattern
    515         //   but is equivalent to the "medium" default format.
    516         if (customFormatArgStarts != null) {
    517             throw new IllegalStateException(
    518                     "toPattern() is not supported after custom Format objects "+
    519                     "have been set via setFormat() or similar APIs");
    520         }
    521         if (msgPattern == null) {
    522             return "";
    523         }
    524         String originalPattern = msgPattern.getPatternString();
    525         return originalPattern == null ? "" : originalPattern;
    526     }
    527 
    528     /**
    529      * Returns the part index of the next ARG_START after partIndex, or -1 if there is none more.
    530      * @param partIndex Part index of the previous ARG_START (initially 0).
    531      */
    532     private int nextTopLevelArgStart(int partIndex) {
    533         if (partIndex != 0) {
    534             partIndex = msgPattern.getLimitPartIndex(partIndex);
    535         }
    536         for (;;) {
    537             MessagePattern.Part.Type type = msgPattern.getPartType(++partIndex);
    538             if (type == MessagePattern.Part.Type.ARG_START) {
    539                 return partIndex;
    540             }
    541             if (type == MessagePattern.Part.Type.MSG_LIMIT) {
    542                 return -1;
    543             }
    544         }
    545     }
    546 
    547     private boolean argNameMatches(int partIndex, String argName, int argNumber) {
    548         Part part = msgPattern.getPart(partIndex);
    549         return part.getType() == MessagePattern.Part.Type.ARG_NAME ?
    550             msgPattern.partSubstringMatches(part, argName) :
    551             part.getValue() == argNumber;  // ARG_NUMBER
    552     }
    553 
    554     private String getArgName(int partIndex) {
    555         Part part = msgPattern.getPart(partIndex);
    556         if (part.getType() == MessagePattern.Part.Type.ARG_NAME) {
    557             return msgPattern.getSubstring(part);
    558         } else {
    559             return Integer.toString(part.getValue());
    560         }
    561     }
    562 
    563     /**
    564      * Sets the Format objects to use for the values passed into
    565      * <code>format</code> methods or returned from <code>parse</code>
    566      * methods. The indices of elements in <code>newFormats</code>
    567      * correspond to the argument indices used in the previously set
    568      * pattern string.
    569      * The order of formats in <code>newFormats</code> thus corresponds to
    570      * the order of elements in the <code>arguments</code> array passed
    571      * to the <code>format</code> methods or the result array returned
    572      * by the <code>parse</code> methods.
    573      * <p>
    574      * If an argument index is used for more than one format element
    575      * in the pattern string, then the corresponding new format is used
    576      * for all such format elements. If an argument index is not used
    577      * for any format element in the pattern string, then the
    578      * corresponding new format is ignored. If fewer formats are provided
    579      * than needed, then only the formats for argument indices less
    580      * than <code>newFormats.length</code> are replaced.
    581      *
    582      * This method is only supported if the format does not use
    583      * named arguments, otherwise an IllegalArgumentException is thrown.
    584      *
    585      * @param newFormats the new formats to use
    586      * @throws NullPointerException if <code>newFormats</code> is null
    587      * @throws IllegalArgumentException if this formatter uses named arguments
    588      * @stable ICU 3.0
    589      */
    590     public void setFormatsByArgumentIndex(Format[] newFormats) {
    591         if (msgPattern.hasNamedArguments()) {
    592             throw new IllegalArgumentException(
    593                     "This method is not available in MessageFormat objects " +
    594                     "that use alphanumeric argument names.");
    595         }
    596         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    597             int argNumber = msgPattern.getPart(partIndex + 1).getValue();
    598             if (argNumber < newFormats.length) {
    599                 setCustomArgStartFormat(partIndex, newFormats[argNumber]);
    600             }
    601         }
    602     }
    603 
    604     /**
    605      * {@icu} Sets the Format objects to use for the values passed into
    606      * <code>format</code> methods or returned from <code>parse</code>
    607      * methods. The keys in <code>newFormats</code> are the argument
    608      * names in the previously set pattern string, and the values
    609      * are the formats.
    610      * <p>
    611      * Only argument names from the pattern string are considered.
    612      * Extra keys in <code>newFormats</code> that do not correspond
    613      * to an argument name are ignored.  Similarly, if there is no
    614      * format in newFormats for an argument name, the formatter
    615      * for that argument remains unchanged.
    616      * <p>
    617      * This may be called on formats that do not use named arguments.
    618      * In this case the map will be queried for key Strings that
    619      * represent argument indices, e.g. "0", "1", "2" etc.
    620      *
    621      * @param newFormats a map from String to Format providing new
    622      *        formats for named arguments.
    623      * @stable ICU 3.8
    624      */
    625     public void setFormatsByArgumentName(Map<String, Format> newFormats) {
    626         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    627             String key = getArgName(partIndex + 1);
    628             if (newFormats.containsKey(key)) {
    629                 setCustomArgStartFormat(partIndex, newFormats.get(key));
    630             }
    631         }
    632     }
    633 
    634     /**
    635      * Sets the Format objects to use for the format elements in the
    636      * previously set pattern string.
    637      * The order of formats in <code>newFormats</code> corresponds to
    638      * the order of format elements in the pattern string.
    639      * <p>
    640      * If more formats are provided than needed by the pattern string,
    641      * the remaining ones are ignored. If fewer formats are provided
    642      * than needed, then only the first <code>newFormats.length</code>
    643      * formats are replaced.
    644      * <p>
    645      * Since the order of format elements in a pattern string often
    646      * changes during localization, it is generally better to use the
    647      * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}
    648      * method, which assumes an order of formats corresponding to the
    649      * order of elements in the <code>arguments</code> array passed to
    650      * the <code>format</code> methods or the result array returned by
    651      * the <code>parse</code> methods.
    652      *
    653      * @param newFormats the new formats to use
    654      * @exception NullPointerException if <code>newFormats</code> is null
    655      * @stable ICU 3.0
    656      */
    657     public void setFormats(Format[] newFormats) {
    658         int formatNumber = 0;
    659         for (int partIndex = 0;
    660                 formatNumber < newFormats.length &&
    661                 (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    662             setCustomArgStartFormat(partIndex, newFormats[formatNumber]);
    663             ++formatNumber;
    664         }
    665     }
    666 
    667     /**
    668      * Sets the Format object to use for the format elements within the
    669      * previously set pattern string that use the given argument
    670      * index.
    671      * The argument index is part of the format element definition and
    672      * represents an index into the <code>arguments</code> array passed
    673      * to the <code>format</code> methods or the result array returned
    674      * by the <code>parse</code> methods.
    675      * <p>
    676      * If the argument index is used for more than one format element
    677      * in the pattern string, then the new format is used for all such
    678      * format elements. If the argument index is not used for any format
    679      * element in the pattern string, then the new format is ignored.
    680      *
    681      * This method is only supported when exclusively numbers are used for
    682      * argument names. Otherwise an IllegalArgumentException is thrown.
    683      *
    684      * @param argumentIndex the argument index for which to use the new format
    685      * @param newFormat the new format to use
    686      * @throws IllegalArgumentException if this format uses named arguments
    687      * @stable ICU 3.0
    688      */
    689     public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) {
    690         if (msgPattern.hasNamedArguments()) {
    691             throw new IllegalArgumentException(
    692                     "This method is not available in MessageFormat objects " +
    693                     "that use alphanumeric argument names.");
    694         }
    695         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    696             if (msgPattern.getPart(partIndex + 1).getValue() == argumentIndex) {
    697                 setCustomArgStartFormat(partIndex, newFormat);
    698             }
    699         }
    700     }
    701 
    702     /**
    703      * {@icu} Sets the Format object to use for the format elements within the
    704      * previously set pattern string that use the given argument
    705      * name.
    706      * <p>
    707      * If the argument name is used for more than one format element
    708      * in the pattern string, then the new format is used for all such
    709      * format elements. If the argument name is not used for any format
    710      * element in the pattern string, then the new format is ignored.
    711      * <p>
    712      * This API may be used on formats that do not use named arguments.
    713      * In this case <code>argumentName</code> should be a String that names
    714      * an argument index, e.g. "0", "1", "2"... etc.  If it does not name
    715      * a valid index, the format will be ignored.  No error is thrown.
    716      *
    717      * @param argumentName the name of the argument to change
    718      * @param newFormat the new format to use
    719      * @stable ICU 3.8
    720      */
    721     public void setFormatByArgumentName(String argumentName, Format newFormat) {
    722         int argNumber = MessagePattern.validateArgumentName(argumentName);
    723         if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) {
    724             return;
    725         }
    726         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    727             if (argNameMatches(partIndex + 1, argumentName, argNumber)) {
    728                 setCustomArgStartFormat(partIndex, newFormat);
    729             }
    730         }
    731     }
    732 
    733     /**
    734      * Sets the Format object to use for the format element with the given
    735      * format element index within the previously set pattern string.
    736      * The format element index is the zero-based number of the format
    737      * element counting from the start of the pattern string.
    738      * <p>
    739      * Since the order of format elements in a pattern string often
    740      * changes during localization, it is generally better to use the
    741      * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}
    742      * method, which accesses format elements based on the argument
    743      * index they specify.
    744      *
    745      * @param formatElementIndex the index of a format element within the pattern
    746      * @param newFormat the format to use for the specified format element
    747      * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or
    748      *            larger than the number of format elements in the pattern string
    749      * @stable ICU 3.0
    750      */
    751     public void setFormat(int formatElementIndex, Format newFormat) {
    752         int formatNumber = 0;
    753         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    754             if (formatNumber == formatElementIndex) {
    755                 setCustomArgStartFormat(partIndex, newFormat);
    756                 return;
    757             }
    758             ++formatNumber;
    759         }
    760         throw new ArrayIndexOutOfBoundsException(formatElementIndex);
    761     }
    762 
    763     /**
    764      * Returns the Format objects used for the values passed into
    765      * <code>format</code> methods or returned from <code>parse</code>
    766      * methods. The indices of elements in the returned array
    767      * correspond to the argument indices used in the previously set
    768      * pattern string.
    769      * The order of formats in the returned array thus corresponds to
    770      * the order of elements in the <code>arguments</code> array passed
    771      * to the <code>format</code> methods or the result array returned
    772      * by the <code>parse</code> methods.
    773      * <p>
    774      * If an argument index is used for more than one format element
    775      * in the pattern string, then the format used for the last such
    776      * format element is returned in the array. If an argument index
    777      * is not used for any format element in the pattern string, then
    778      * null is returned in the array.
    779      *
    780      * This method is only supported when exclusively numbers are used for
    781      * argument names. Otherwise an IllegalArgumentException is thrown.
    782      *
    783      * @return the formats used for the arguments within the pattern
    784      * @throws IllegalArgumentException if this format uses named arguments
    785      * @stable ICU 3.0
    786      */
    787     public Format[] getFormatsByArgumentIndex() {
    788         if (msgPattern.hasNamedArguments()) {
    789             throw new IllegalArgumentException(
    790                     "This method is not available in MessageFormat objects " +
    791                     "that use alphanumeric argument names.");
    792         }
    793         ArrayList<Format> list = new ArrayList<Format>();
    794         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    795             int argNumber = msgPattern.getPart(partIndex + 1).getValue();
    796             while (argNumber >= list.size()) {
    797                 list.add(null);
    798             }
    799             list.set(argNumber, cachedFormatters == null ? null : cachedFormatters.get(partIndex));
    800         }
    801         return list.toArray(new Format[list.size()]);
    802     }
    803 
    804     /**
    805      * Returns the Format objects used for the format elements in the
    806      * previously set pattern string.
    807      * The order of formats in the returned array corresponds to
    808      * the order of format elements in the pattern string.
    809      * <p>
    810      * Since the order of format elements in a pattern string often
    811      * changes during localization, it's generally better to use the
    812      * {@link #getFormatsByArgumentIndex()}
    813      * method, which assumes an order of formats corresponding to the
    814      * order of elements in the <code>arguments</code> array passed to
    815      * the <code>format</code> methods or the result array returned by
    816      * the <code>parse</code> methods.
    817      *
    818      * This method is only supported when exclusively numbers are used for
    819      * argument names. Otherwise an IllegalArgumentException is thrown.
    820      *
    821      * @return the formats used for the format elements in the pattern
    822      * @throws IllegalArgumentException if this format uses named arguments
    823      * @stable ICU 3.0
    824      */
    825     public Format[] getFormats() {
    826         ArrayList<Format> list = new ArrayList<Format>();
    827         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    828             list.add(cachedFormatters == null ? null : cachedFormatters.get(partIndex));
    829         }
    830         return list.toArray(new Format[list.size()]);
    831     }
    832 
    833     /**
    834      * {@icu} Returns the top-level argument names. For more details, see
    835      * {@link #setFormatByArgumentName(String, Format)}.
    836      * @return a Set of argument names
    837      * @stable ICU 4.8
    838      */
    839     public Set<String> getArgumentNames() {
    840         Set<String> result = new HashSet<String>();
    841         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    842             result.add(getArgName(partIndex + 1));
    843         }
    844         return result;
    845     }
    846 
    847     /**
    848      * {@icu} Returns the first top-level format associated with the given argument name.
    849      * For more details, see {@link #setFormatByArgumentName(String, Format)}.
    850      * @param argumentName The name of the desired argument.
    851      * @return the Format associated with the name, or null if there isn't one.
    852      * @stable ICU 4.8
    853      */
    854     public Format getFormatByArgumentName(String argumentName) {
    855         if (cachedFormatters == null) {
    856             return null;
    857         }
    858         int argNumber = MessagePattern.validateArgumentName(argumentName);
    859         if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) {
    860             return null;
    861         }
    862         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
    863             if (argNameMatches(partIndex + 1, argumentName, argNumber)) {
    864                 return cachedFormatters.get(partIndex);
    865             }
    866         }
    867         return null;
    868     }
    869 
    870     /**
    871      * Formats an array of objects and appends the <code>MessageFormat</code>'s
    872      * pattern, with arguments replaced by the formatted objects, to the
    873      * provided <code>StringBuffer</code>.
    874      * <p>
    875      * The text substituted for the individual format elements is derived from
    876      * the current subformat of the format element and the
    877      * <code>arguments</code> element at the format element's argument index
    878      * as indicated by the first matching line of the following table. An
    879      * argument is <i>unavailable</i> if <code>arguments</code> is
    880      * <code>null</code> or has fewer than argumentIndex+1 elements.  When
    881      * an argument is unavailable no substitution is performed.
    882      *
    883      * <table border=1>
    884      *    <tr>
    885      *       <th>argType or Format
    886      *       <th>value object
    887      *       <th>Formatted Text
    888      *    <tr>
    889      *       <td><i>any</i>
    890      *       <td><i>unavailable</i>
    891      *       <td><code>"{" + argNameOrNumber + "}"</code>
    892      *    <tr>
    893      *       <td><i>any</i>
    894      *       <td><code>null</code>
    895      *       <td><code>"null"</code>
    896      *    <tr>
    897      *       <td>custom Format <code>!= null</code>
    898      *       <td><i>any</i>
    899      *       <td><code>customFormat.format(argument)</code>
    900      *    <tr>
    901      *       <td>noneArg, or custom Format <code>== null</code>
    902      *       <td><code>instanceof Number</code>
    903      *       <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
    904      *    <tr>
    905      *       <td>noneArg, or custom Format <code>== null</code>
    906      *       <td><code>instanceof Date</code>
    907      *       <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT,
    908      *           DateFormat.SHORT, getLocale()).format(argument)</code>
    909      *    <tr>
    910      *       <td>noneArg, or custom Format <code>== null</code>
    911      *       <td><code>instanceof String</code>
    912      *       <td><code>argument</code>
    913      *    <tr>
    914      *       <td>noneArg, or custom Format <code>== null</code>
    915      *       <td><i>any</i>
    916      *       <td><code>argument.toString()</code>
    917      *    <tr>
    918      *       <td>complexArg
    919      *       <td><i>any</i>
    920      *       <td>result of recursive formatting of a selected sub-message
    921      * </table>
    922      * <p>
    923      * If <code>pos</code> is non-null, and refers to
    924      * <code>Field.ARGUMENT</code>, the location of the first formatted
    925      * string will be returned.
    926      *
    927      * This method is only supported when the format does not use named
    928      * arguments, otherwise an IllegalArgumentException is thrown.
    929      *
    930      * @param arguments an array of objects to be formatted and substituted.
    931      * @param result where text is appended.
    932      * @param pos On input: an alignment field, if desired.
    933      *            On output: the offsets of the alignment field.
    934      * @throws IllegalArgumentException if a value in the
    935      *         <code>arguments</code> array is not of the type
    936      *         expected by the corresponding argument or custom Format object.
    937      * @throws IllegalArgumentException if this format uses named arguments
    938      * @stable ICU 3.0
    939      */
    940     public final StringBuffer format(Object[] arguments, StringBuffer result,
    941                                      FieldPosition pos)
    942     {
    943         format(arguments, null, new AppendableWrapper(result), pos);
    944         return result;
    945     }
    946 
    947     /**
    948      * Formats a map of objects and appends the <code>MessageFormat</code>'s
    949      * pattern, with arguments replaced by the formatted objects, to the
    950      * provided <code>StringBuffer</code>.
    951      * <p>
    952      * The text substituted for the individual format elements is derived from
    953      * the current subformat of the format element and the
    954      * <code>arguments</code> value corresopnding to the format element's
    955      * argument name.
    956      * <p>
    957      * A numbered pattern argument is matched with a map key that contains that number
    958      * as an ASCII-decimal-digit string (without leading zero).
    959      * <p>
    960      * An argument is <i>unavailable</i> if <code>arguments</code> is
    961      * <code>null</code> or does not have a value corresponding to an argument
    962      * name in the pattern.  When an argument is unavailable no substitution
    963      * is performed.
    964      *
    965      * @param arguments a map of objects to be formatted and substituted.
    966      * @param result where text is appended.
    967      * @param pos On input: an alignment field, if desired.
    968      *            On output: the offsets of the alignment field.
    969      * @throws IllegalArgumentException if a value in the
    970      *         <code>arguments</code> array is not of the type
    971      *         expected by the corresponding argument or custom Format object.
    972      * @return the passed-in StringBuffer
    973      * @stable ICU 3.8
    974      */
    975     public final StringBuffer format(Map<String, Object> arguments, StringBuffer result,
    976                                      FieldPosition pos) {
    977         format(null, arguments, new AppendableWrapper(result), pos);
    978         return result;
    979     }
    980 
    981     /**
    982      * Creates a MessageFormat with the given pattern and uses it
    983      * to format the given arguments. This is equivalent to
    984      * <blockquote>
    985      *     <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link
    986      *     #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition)
    987      *     format}(arguments, new StringBuffer(), null).toString()</code>
    988      * </blockquote>
    989      *
    990      * @throws IllegalArgumentException if the pattern is invalid
    991      * @throws IllegalArgumentException if a value in the
    992      *         <code>arguments</code> array is not of the type
    993      *         expected by the corresponding argument or custom Format object.
    994      * @throws IllegalArgumentException if this format uses named arguments
    995      * @stable ICU 3.0
    996      */
    997     public static String format(String pattern, Object... arguments) {
    998         MessageFormat temp = new MessageFormat(pattern);
    999         return temp.format(arguments);
   1000     }
   1001 
   1002     /**
   1003      * Creates a MessageFormat with the given pattern and uses it to
   1004      * format the given arguments.  The pattern must identifyarguments
   1005      * by name instead of by number.
   1006      * <p>
   1007      * @throws IllegalArgumentException if the pattern is invalid
   1008      * @throws IllegalArgumentException if a value in the
   1009      *         <code>arguments</code> array is not of the type
   1010      *         expected by the corresponding argument or custom Format object.
   1011      * @see #format(Map, StringBuffer, FieldPosition)
   1012      * @see #format(String, Object[])
   1013      * @stable ICU 3.8
   1014      */
   1015     public static String format(String pattern, Map<String, Object> arguments) {
   1016         MessageFormat temp = new MessageFormat(pattern);
   1017         return temp.format(arguments);
   1018     }
   1019 
   1020     /**
   1021      * {@icu} Returns true if this MessageFormat uses named arguments,
   1022      * and false otherwise.  See class description.
   1023      *
   1024      * @return true if named arguments are used.
   1025      * @stable ICU 3.8
   1026      */
   1027     public boolean usesNamedArguments() {
   1028         return msgPattern.hasNamedArguments();
   1029     }
   1030 
   1031     // Overrides
   1032     /**
   1033      * Formats a map or array of objects and appends the <code>MessageFormat</code>'s
   1034      * pattern, with format elements replaced by the formatted objects, to the
   1035      * provided <code>StringBuffer</code>.
   1036      * This is equivalent to either of
   1037      * <blockquote>
   1038      *     <code>{@link #format(java.lang.Object[], java.lang.StringBuffer,
   1039      *     java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
   1040      *     <code>{@link #format(java.util.Map, java.lang.StringBuffer,
   1041      *     java.text.FieldPosition) format}((Map) arguments, result, pos)</code>
   1042      * </blockquote>
   1043      * A map must be provided if this format uses named arguments, otherwise
   1044      * an IllegalArgumentException will be thrown.
   1045      * @param arguments a map or array of objects to be formatted
   1046      * @param result where text is appended
   1047      * @param pos On input: an alignment field, if desired
   1048      *            On output: the offsets of the alignment field
   1049      * @throws IllegalArgumentException if an argument in
   1050      *         <code>arguments</code> is not of the type
   1051      *         expected by the format element(s) that use it
   1052      * @throws IllegalArgumentException if <code>arguments</code> is
   1053      *         an array of Object and this format uses named arguments
   1054      * @stable ICU 3.0
   1055      */
   1056     @Override
   1057     public final StringBuffer format(Object arguments, StringBuffer result,
   1058                                      FieldPosition pos)
   1059     {
   1060         format(arguments, new AppendableWrapper(result), pos);
   1061         return result;
   1062     }
   1063 
   1064     /**
   1065      * Formats an array of objects and inserts them into the
   1066      * <code>MessageFormat</code>'s pattern, producing an
   1067      * <code>AttributedCharacterIterator</code>.
   1068      * You can use the returned <code>AttributedCharacterIterator</code>
   1069      * to build the resulting String, as well as to determine information
   1070      * about the resulting String.
   1071      * <p>
   1072      * The text of the returned <code>AttributedCharacterIterator</code> is
   1073      * the same that would be returned by
   1074      * <blockquote>
   1075      *     <code>{@link #format(java.lang.Object[], java.lang.StringBuffer,
   1076      *     java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
   1077      * </blockquote>
   1078      * <p>
   1079      * In addition, the <code>AttributedCharacterIterator</code> contains at
   1080      * least attributes indicating where text was generated from an
   1081      * argument in the <code>arguments</code> array. The keys of these attributes are of
   1082      * type <code>MessageFormat.Field</code>, their values are
   1083      * <code>Integer</code> objects indicating the index in the <code>arguments</code>
   1084      * array of the argument from which the text was generated.
   1085      * <p>
   1086      * The attributes/value from the underlying <code>Format</code>
   1087      * instances that <code>MessageFormat</code> uses will also be
   1088      * placed in the resulting <code>AttributedCharacterIterator</code>.
   1089      * This allows you to not only find where an argument is placed in the
   1090      * resulting String, but also which fields it contains in turn.
   1091      *
   1092      * @param arguments an array of objects to be formatted and substituted.
   1093      * @return AttributedCharacterIterator describing the formatted value.
   1094      * @exception NullPointerException if <code>arguments</code> is null.
   1095      * @throws IllegalArgumentException if a value in the
   1096      *         <code>arguments</code> array is not of the type
   1097      *         expected by the corresponding argument or custom Format object.
   1098      * @stable ICU 3.8
   1099      */
   1100     @Override
   1101     public AttributedCharacterIterator formatToCharacterIterator(Object arguments) {
   1102         if (arguments == null) {
   1103             throw new NullPointerException(
   1104                    "formatToCharacterIterator must be passed non-null object");
   1105         }
   1106         StringBuilder result = new StringBuilder();
   1107         AppendableWrapper wrapper = new AppendableWrapper(result);
   1108         wrapper.useAttributes();
   1109         format(arguments, wrapper, null);
   1110         AttributedString as = new AttributedString(result.toString());
   1111         for (AttributeAndPosition a : wrapper.attributes) {
   1112             as.addAttribute(a.key, a.value, a.start, a.limit);
   1113         }
   1114         return as.getIterator();
   1115     }
   1116 
   1117     /**
   1118      * Parses the string.
   1119      *
   1120      * <p>Caveats: The parse may fail in a number of circumstances.
   1121      * For example:
   1122      * <ul>
   1123      * <li>If one of the arguments does not occur in the pattern.
   1124      * <li>If the format of an argument loses information, such as
   1125      *     with a choice format where a large number formats to "many".
   1126      * <li>Does not yet handle recursion (where
   1127      *     the substituted strings contain {n} references.)
   1128      * <li>Will not always find a match (or the correct match)
   1129      *     if some part of the parse is ambiguous.
   1130      *     For example, if the pattern "{1},{2}" is used with the
   1131      *     string arguments {"a,b", "c"}, it will format as "a,b,c".
   1132      *     When the result is parsed, it will return {"a", "b,c"}.
   1133      * <li>If a single argument is parsed more than once in the string,
   1134      *     then the later parse wins.
   1135      * </ul>
   1136      * When the parse fails, use ParsePosition.getErrorIndex() to find out
   1137      * where in the string did the parsing failed. The returned error
   1138      * index is the starting offset of the sub-patterns that the string
   1139      * is comparing with. For example, if the parsing string "AAA {0} BBB"
   1140      * is comparing against the pattern "AAD {0} BBB", the error index is
   1141      * 0. When an error occurs, the call to this method will return null.
   1142      * If the source is null, return an empty array.
   1143      *
   1144      * @throws IllegalArgumentException if this format uses named arguments
   1145      * @stable ICU 3.0
   1146      */
   1147     public Object[] parse(String source, ParsePosition pos) {
   1148         if (msgPattern.hasNamedArguments()) {
   1149             throw new IllegalArgumentException(
   1150                     "This method is not available in MessageFormat objects " +
   1151                     "that use named argument.");
   1152         }
   1153 
   1154         // Count how many slots we need in the array.
   1155         int maxArgId = -1;
   1156         for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
   1157             int argNumber=msgPattern.getPart(partIndex + 1).getValue();
   1158             if (argNumber > maxArgId) {
   1159                 maxArgId = argNumber;
   1160             }
   1161         }
   1162         Object[] resultArray = new Object[maxArgId + 1];
   1163 
   1164         int backupStartPos = pos.getIndex();
   1165         parse(0, source, pos, resultArray, null);
   1166         if (pos.getIndex() == backupStartPos) { // unchanged, returned object is null
   1167             return null;
   1168         }
   1169 
   1170         return resultArray;
   1171     }
   1172 
   1173     /**
   1174      * {@icu} Parses the string, returning the results in a Map.
   1175      * This is similar to the version that returns an array
   1176      * of Object.  This supports both named and numbered
   1177      * arguments-- if numbered, the keys in the map are the
   1178      * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...).
   1179      *
   1180      * @param source the text to parse
   1181      * @param pos the position at which to start parsing.  on return,
   1182      *        contains the result of the parse.
   1183      * @return a Map containing key/value pairs for each parsed argument.
   1184      * @stable ICU 3.8
   1185      */
   1186     public Map<String, Object> parseToMap(String source, ParsePosition pos)  {
   1187         Map<String, Object> result = new HashMap<String, Object>();
   1188         int backupStartPos = pos.getIndex();
   1189         parse(0, source, pos, null, result);
   1190         if (pos.getIndex() == backupStartPos) {
   1191             return null;
   1192         }
   1193         return result;
   1194     }
   1195 
   1196     /**
   1197      * Parses text from the beginning of the given string to produce an object
   1198      * array.
   1199      * The method may not use the entire text of the given string.
   1200      * <p>
   1201      * See the {@link #parse(String, ParsePosition)} method for more information
   1202      * on message parsing.
   1203      *
   1204      * @param source A <code>String</code> whose beginning should be parsed.
   1205      * @return An <code>Object</code> array parsed from the string.
   1206      * @exception ParseException if the beginning of the specified string cannot be parsed.
   1207      * @exception IllegalArgumentException if this format uses named arguments
   1208      * @stable ICU 3.0
   1209      */
   1210     public Object[] parse(String source) throws ParseException {
   1211         ParsePosition pos = new ParsePosition(0);
   1212         Object[] result = parse(source, pos);
   1213         if (pos.getIndex() == 0) // unchanged, returned object is null
   1214             throw new ParseException("MessageFormat parse error!",
   1215                                      pos.getErrorIndex());
   1216 
   1217         return result;
   1218     }
   1219 
   1220     /**
   1221      * Parses the string, filling either the Map or the Array.
   1222      * This is a private method that all the public parsing methods call.
   1223      * This supports both named and numbered
   1224      * arguments-- if numbered, the keys in the map are the
   1225      * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...).
   1226      *
   1227      * @param msgStart index in the message pattern to start from.
   1228      * @param source the text to parse
   1229      * @param pos the position at which to start parsing.  on return,
   1230      *        contains the result of the parse.
   1231      * @param args if not null, the parse results will be filled here (The pattern
   1232      *        has to have numbered arguments in order for this to not be null).
   1233      * @param argsMap if not null, the parse results will be filled here.
   1234      */
   1235     private void parse(int msgStart, String source, ParsePosition pos,
   1236                        Object[] args, Map<String, Object> argsMap) {
   1237         if (source == null) {
   1238             return;
   1239         }
   1240         String msgString=msgPattern.getPatternString();
   1241         int prevIndex=msgPattern.getPart(msgStart).getLimit();
   1242         int sourceOffset = pos.getIndex();
   1243         ParsePosition tempStatus = new ParsePosition(0);
   1244 
   1245         for(int i=msgStart+1; ; ++i) {
   1246             Part part=msgPattern.getPart(i);
   1247             Part.Type type=part.getType();
   1248             int index=part.getIndex();
   1249             // Make sure the literal string matches.
   1250             int len = index - prevIndex;
   1251             if (len == 0 || msgString.regionMatches(prevIndex, source, sourceOffset, len)) {
   1252                 sourceOffset += len;
   1253                 prevIndex += len;
   1254             } else {
   1255                 pos.setErrorIndex(sourceOffset);
   1256                 return; // leave index as is to signal error
   1257             }
   1258             if(type==Part.Type.MSG_LIMIT) {
   1259                 // Things went well! Done.
   1260                 pos.setIndex(sourceOffset);
   1261                 return;
   1262             }
   1263             if(type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR) {
   1264                 prevIndex=part.getLimit();
   1265                 continue;
   1266             }
   1267             // We do not support parsing Plural formats. (No REPLACE_NUMBER here.)
   1268             assert type==Part.Type.ARG_START : "Unexpected Part "+part+" in parsed message.";
   1269             int argLimit=msgPattern.getLimitPartIndex(i);
   1270 
   1271             ArgType argType=part.getArgType();
   1272             part=msgPattern.getPart(++i);
   1273             // Compute the argId, so we can use it as a key.
   1274             Object argId=null;
   1275             int argNumber = 0;
   1276             String key = null;
   1277             if(args!=null) {
   1278                 argNumber=part.getValue();  // ARG_NUMBER
   1279                 argId = Integer.valueOf(argNumber);
   1280             } else {
   1281                 if(part.getType()==MessagePattern.Part.Type.ARG_NAME) {
   1282                     key=msgPattern.getSubstring(part);
   1283                 } else /* ARG_NUMBER */ {
   1284                     key=Integer.toString(part.getValue());
   1285                 }
   1286                 argId = key;
   1287             }
   1288 
   1289             ++i;
   1290             Format formatter = null;
   1291             boolean haveArgResult = false;
   1292             Object argResult = null;
   1293             if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) {
   1294                 // Just parse using the formatter.
   1295                 tempStatus.setIndex(sourceOffset);
   1296                 argResult = formatter.parseObject(source, tempStatus);
   1297                 if (tempStatus.getIndex() == sourceOffset) {
   1298                     pos.setErrorIndex(sourceOffset);
   1299                     return; // leave index as is to signal error
   1300                 }
   1301                 haveArgResult = true;
   1302                 sourceOffset = tempStatus.getIndex();
   1303             } else if(
   1304                     argType==ArgType.NONE ||
   1305                     (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) {
   1306                 // Match as a string.
   1307                 // if at end, use longest possible match
   1308                 // otherwise uses first match to intervening string
   1309                 // does NOT recursively try all possibilities
   1310                 String stringAfterArgument = getLiteralStringUntilNextArgument(argLimit);
   1311                 int next;
   1312                 if (stringAfterArgument.length() != 0) {
   1313                     next = source.indexOf(stringAfterArgument, sourceOffset);
   1314                 } else {
   1315                     next = source.length();
   1316                 }
   1317                 if (next < 0) {
   1318                     pos.setErrorIndex(sourceOffset);
   1319                     return; // leave index as is to signal error
   1320                 } else {
   1321                     String strValue = source.substring(sourceOffset, next);
   1322                     if (!strValue.equals("{" + argId.toString() + "}")) {
   1323                         haveArgResult = true;
   1324                         argResult = strValue;
   1325                     }
   1326                     sourceOffset = next;
   1327                 }
   1328             } else if(argType==ArgType.CHOICE) {
   1329                 tempStatus.setIndex(sourceOffset);
   1330                 double choiceResult = parseChoiceArgument(msgPattern, i, source, tempStatus);
   1331                 if (tempStatus.getIndex() == sourceOffset) {
   1332                     pos.setErrorIndex(sourceOffset);
   1333                     return; // leave index as is to signal error
   1334                 }
   1335                 argResult = choiceResult;
   1336                 haveArgResult = true;
   1337                 sourceOffset = tempStatus.getIndex();
   1338             } else if(argType.hasPluralStyle() || argType==ArgType.SELECT) {
   1339                 // No can do!
   1340                 throw new UnsupportedOperationException(
   1341                         "Parsing of plural/select/selectordinal argument is not supported.");
   1342             } else {
   1343                 // This should never happen.
   1344                 throw new IllegalStateException("unexpected argType "+argType);
   1345             }
   1346             if (haveArgResult) {
   1347                 if (args != null) {
   1348                     args[argNumber] = argResult;
   1349                 } else if (argsMap != null) {
   1350                     argsMap.put(key, argResult);
   1351                 }
   1352             }
   1353             prevIndex=msgPattern.getPart(argLimit).getLimit();
   1354             i=argLimit;
   1355         }
   1356     }
   1357 
   1358     /**
   1359      * {@icu} Parses text from the beginning of the given string to produce a map from
   1360      * argument to values. The method may not use the entire text of the given string.
   1361      *
   1362      * <p>See the {@link #parse(String, ParsePosition)} method for more information on
   1363      * message parsing.
   1364      *
   1365      * @param source A <code>String</code> whose beginning should be parsed.
   1366      * @return A <code>Map</code> parsed from the string.
   1367      * @throws ParseException if the beginning of the specified string cannot
   1368      *         be parsed.
   1369      * @see #parseToMap(String, ParsePosition)
   1370      * @stable ICU 3.8
   1371      */
   1372     public Map<String, Object> parseToMap(String source) throws ParseException {
   1373         ParsePosition pos = new ParsePosition(0);
   1374         Map<String, Object> result = new HashMap<String, Object>();
   1375         parse(0, source, pos, null, result);
   1376         if (pos.getIndex() == 0) // unchanged, returned object is null
   1377             throw new ParseException("MessageFormat parse error!",
   1378                                      pos.getErrorIndex());
   1379 
   1380         return result;
   1381     }
   1382 
   1383     /**
   1384      * Parses text from a string to produce an object array or Map.
   1385      * <p>
   1386      * The method attempts to parse text starting at the index given by
   1387      * <code>pos</code>.
   1388      * If parsing succeeds, then the index of <code>pos</code> is updated
   1389      * to the index after the last character used (parsing does not necessarily
   1390      * use all characters up to the end of the string), and the parsed
   1391      * object array is returned. The updated <code>pos</code> can be used to
   1392      * indicate the starting point for the next call to this method.
   1393      * If an error occurs, then the index of <code>pos</code> is not
   1394      * changed, the error index of <code>pos</code> is set to the index of
   1395      * the character where the error occurred, and null is returned.
   1396      * <p>
   1397      * See the {@link #parse(String, ParsePosition)} method for more information
   1398      * on message parsing.
   1399      *
   1400      * @param source A <code>String</code>, part of which should be parsed.
   1401      * @param pos A <code>ParsePosition</code> object with index and error
   1402      *            index information as described above.
   1403      * @return An <code>Object</code> parsed from the string, either an
   1404      *         array of Object, or a Map, depending on whether named
   1405      *         arguments are used.  This can be queried using <code>usesNamedArguments</code>.
   1406      *         In case of error, returns null.
   1407      * @throws NullPointerException if <code>pos</code> is null.
   1408      * @stable ICU 3.0
   1409      */
   1410     @Override
   1411     public Object parseObject(String source, ParsePosition pos) {
   1412         if (!msgPattern.hasNamedArguments()) {
   1413             return parse(source, pos);
   1414         } else {
   1415             return parseToMap(source, pos);
   1416         }
   1417     }
   1418 
   1419     /**
   1420      * {@inheritDoc}
   1421      * @stable ICU 3.0
   1422      */
   1423     @Override
   1424     public Object clone() {
   1425         MessageFormat other = (MessageFormat) super.clone();
   1426 
   1427         if (customFormatArgStarts != null) {
   1428             other.customFormatArgStarts = new HashSet<Integer>();
   1429             for (Integer key : customFormatArgStarts) {
   1430                 other.customFormatArgStarts.add(key);
   1431             }
   1432         } else {
   1433             other.customFormatArgStarts = null;
   1434         }
   1435 
   1436         if (cachedFormatters != null) {
   1437             other.cachedFormatters = new HashMap<Integer, Format>();
   1438             Iterator<Map.Entry<Integer, Format>> it = cachedFormatters.entrySet().iterator();
   1439             while (it.hasNext()){
   1440                 Map.Entry<Integer, Format> entry = it.next();
   1441                 other.cachedFormatters.put(entry.getKey(), entry.getValue());
   1442             }
   1443         } else {
   1444             other.cachedFormatters = null;
   1445         }
   1446 
   1447         other.msgPattern = msgPattern == null ? null : (MessagePattern)msgPattern.clone();
   1448         other.stockDateFormatter =
   1449                 stockDateFormatter == null ? null : (DateFormat) stockDateFormatter.clone();
   1450         other.stockNumberFormatter =
   1451                 stockNumberFormatter == null ? null : (NumberFormat) stockNumberFormatter.clone();
   1452 
   1453         other.pluralProvider = null;
   1454         other.ordinalProvider = null;
   1455         return other;
   1456     }
   1457 
   1458     /**
   1459      * {@inheritDoc}
   1460      * @stable ICU 3.0
   1461      */
   1462     @Override
   1463     public boolean equals(Object obj) {
   1464         if (this == obj)                      // quick check
   1465             return true;
   1466         if (obj == null || getClass() != obj.getClass())
   1467             return false;
   1468         MessageFormat other = (MessageFormat) obj;
   1469         return Utility.objectEquals(ulocale, other.ulocale)
   1470                 && Utility.objectEquals(msgPattern, other.msgPattern)
   1471                 && Utility.objectEquals(cachedFormatters, other.cachedFormatters)
   1472                 && Utility.objectEquals(customFormatArgStarts, other.customFormatArgStarts);
   1473         // Note: It might suffice to only compare custom formatters
   1474         // rather than all formatters.
   1475     }
   1476 
   1477     /**
   1478      * {@inheritDoc}
   1479      * @stable ICU 3.0
   1480      */
   1481     @Override
   1482     public int hashCode() {
   1483         return msgPattern.getPatternString().hashCode(); // enough for reasonable distribution
   1484     }
   1485 
   1486     /**
   1487      * Defines constants that are used as attribute keys in the
   1488      * <code>AttributedCharacterIterator</code> returned
   1489      * from <code>MessageFormat.formatToCharacterIterator</code>.
   1490      *
   1491      * @stable ICU 3.8
   1492      */
   1493     public static class Field extends Format.Field {
   1494 
   1495         private static final long serialVersionUID = 7510380454602616157L;
   1496 
   1497         /**
   1498          * Create a <code>Field</code> with the specified name.
   1499          *
   1500          * @param name The name of the attribute
   1501          *
   1502          * @stable ICU 3.8
   1503          */
   1504         protected Field(String name) {
   1505             super(name);
   1506         }
   1507 
   1508         /**
   1509          * Resolves instances being deserialized to the predefined constants.
   1510          *
   1511          * @return resolved MessageFormat.Field constant
   1512          * @throws InvalidObjectException if the constant could not be resolved.
   1513          *
   1514          * @stable ICU 3.8
   1515          */
   1516         @Override
   1517         protected Object readResolve() throws InvalidObjectException {
   1518             if (this.getClass() != MessageFormat.Field.class) {
   1519                 throw new InvalidObjectException(
   1520                     "A subclass of MessageFormat.Field must implement readResolve.");
   1521             }
   1522             if (this.getName().equals(ARGUMENT.getName())) {
   1523                 return ARGUMENT;
   1524             } else {
   1525                 throw new InvalidObjectException("Unknown attribute name.");
   1526             }
   1527         }
   1528 
   1529         /**
   1530          * Constant identifying a portion of a message that was generated
   1531          * from an argument passed into <code>formatToCharacterIterator</code>.
   1532          * The value associated with the key will be an <code>Integer</code>
   1533          * indicating the index in the <code>arguments</code> array of the
   1534          * argument from which the text was generated.
   1535          *
   1536          * @stable ICU 3.8
   1537          */
   1538         public static final Field ARGUMENT = new Field("message argument field");
   1539     }
   1540 
   1541     // ===========================privates============================
   1542 
   1543     // *Important*: All fields must be declared *transient* so that we can fully
   1544     // control serialization!
   1545     // See for example Joshua Bloch's "Effective Java", chapter 10 Serialization.
   1546 
   1547     /**
   1548      * The locale to use for formatting numbers and dates.
   1549      */
   1550     private transient ULocale ulocale;
   1551 
   1552     /**
   1553      * The MessagePattern which contains the parsed structure of the pattern string.
   1554      */
   1555     private transient MessagePattern msgPattern;
   1556     /**
   1557      * Cached formatters so we can just use them whenever needed instead of creating
   1558      * them from scratch every time.
   1559      */
   1560     private transient Map<Integer, Format> cachedFormatters;
   1561     /**
   1562      * Set of ARG_START part indexes where custom, user-provided Format objects
   1563      * have been set via setFormat() or similar API.
   1564      */
   1565     private transient Set<Integer> customFormatArgStarts;
   1566 
   1567     /**
   1568      * Stock formatters. Those are used when a format is not explicitly mentioned in
   1569      * the message. The format is inferred from the argument.
   1570      */
   1571     private transient DateFormat stockDateFormatter;
   1572     private transient NumberFormat stockNumberFormatter;
   1573 
   1574     private transient PluralSelectorProvider pluralProvider;
   1575     private transient PluralSelectorProvider ordinalProvider;
   1576 
   1577     private DateFormat getStockDateFormatter() {
   1578         if (stockDateFormatter == null) {
   1579             stockDateFormatter = DateFormat.getDateTimeInstance(
   1580                     DateFormat.SHORT, DateFormat.SHORT, ulocale);//fix
   1581         }
   1582         return stockDateFormatter;
   1583     }
   1584     private NumberFormat getStockNumberFormatter() {
   1585         if (stockNumberFormatter == null) {
   1586             stockNumberFormatter = NumberFormat.getInstance(ulocale);
   1587         }
   1588         return stockNumberFormatter;
   1589     }
   1590 
   1591     // *Important*: All fields must be declared *transient*.
   1592     // See the longer comment above ulocale.
   1593 
   1594     /**
   1595      * Formats the arguments and writes the result into the
   1596      * AppendableWrapper, updates the field position.
   1597      *
   1598      * <p>Exactly one of args and argsMap must be null, the other non-null.
   1599      *
   1600      * @param msgStart      Index to msgPattern part to start formatting from.
   1601      * @param pluralNumber  null except when formatting a plural argument sub-message
   1602      *                      where a '#' is replaced by the format string for this number.
   1603      * @param args          The formattable objects array. Non-null iff numbered values are used.
   1604      * @param argsMap       The key-value map of formattable objects. Non-null iff named values are used.
   1605      * @param dest          Output parameter to receive the result.
   1606      *                      The result (string & attributes) is appended to existing contents.
   1607      * @param fp            Field position status.
   1608      */
   1609     private void format(int msgStart, PluralSelectorContext pluralNumber,
   1610                         Object[] args, Map<String, Object> argsMap,
   1611                         AppendableWrapper dest, FieldPosition fp) {
   1612         String msgString=msgPattern.getPatternString();
   1613         int prevIndex=msgPattern.getPart(msgStart).getLimit();
   1614         for(int i=msgStart+1;; ++i) {
   1615             Part part=msgPattern.getPart(i);
   1616             Part.Type type=part.getType();
   1617             int index=part.getIndex();
   1618             dest.append(msgString, prevIndex, index);
   1619             if(type==Part.Type.MSG_LIMIT) {
   1620                 return;
   1621             }
   1622             prevIndex=part.getLimit();
   1623             if(type==Part.Type.REPLACE_NUMBER) {
   1624                 if(pluralNumber.forReplaceNumber) {
   1625                     // number-offset was already formatted.
   1626                     dest.formatAndAppend(pluralNumber.formatter,
   1627                             pluralNumber.number, pluralNumber.numberString);
   1628                 } else {
   1629                     dest.formatAndAppend(getStockNumberFormatter(), pluralNumber.number);
   1630                 }
   1631                 continue;
   1632             }
   1633             if(type!=Part.Type.ARG_START) {
   1634                 continue;
   1635             }
   1636             int argLimit=msgPattern.getLimitPartIndex(i);
   1637             ArgType argType=part.getArgType();
   1638             part=msgPattern.getPart(++i);
   1639             Object arg;
   1640             boolean noArg=false;
   1641             Object argId=null;
   1642             String argName=msgPattern.getSubstring(part);
   1643             if(args!=null) {
   1644                 int argNumber=part.getValue();  // ARG_NUMBER
   1645                 if (dest.attributes != null) {
   1646                     // We only need argId if we add it into the attributes.
   1647                     argId = Integer.valueOf(argNumber);
   1648                 }
   1649                 if(0<=argNumber && argNumber<args.length) {
   1650                     arg=args[argNumber];
   1651                 } else {
   1652                     arg=null;
   1653                     noArg=true;
   1654                 }
   1655             } else {
   1656                 argId = argName;
   1657                 if(argsMap!=null && argsMap.containsKey(argName)) {
   1658                     arg=argsMap.get(argName);
   1659                 } else {
   1660                     arg=null;
   1661                     noArg=true;
   1662                 }
   1663             }
   1664             ++i;
   1665             int prevDestLength=dest.length;
   1666             Format formatter = null;
   1667             if (noArg) {
   1668                 dest.append("{"+argName+"}");
   1669             } else if (arg == null) {
   1670                 dest.append("null");
   1671             } else if(pluralNumber!=null && pluralNumber.numberArgIndex==(i-2)) {
   1672                 if(pluralNumber.offset == 0) {
   1673                     // The number was already formatted with this formatter.
   1674                     dest.formatAndAppend(pluralNumber.formatter, pluralNumber.number, pluralNumber.numberString);
   1675                 } else {
   1676                     // Do not use the formatted (number-offset) string for a named argument
   1677                     // that formats the number without subtracting the offset.
   1678                     dest.formatAndAppend(pluralNumber.formatter, arg);
   1679                 }
   1680             } else if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) {
   1681                 // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings.
   1682                 if (    formatter instanceof ChoiceFormat ||
   1683                         formatter instanceof PluralFormat ||
   1684                         formatter instanceof SelectFormat) {
   1685                     // We only handle nested formats here if they were provided via setFormat() or its siblings.
   1686                     // Otherwise they are not cached and instead handled below according to argType.
   1687                     String subMsgString = formatter.format(arg);
   1688                     if (subMsgString.indexOf('{') >= 0 ||
   1689                             (subMsgString.indexOf('\'') >= 0 && !msgPattern.jdkAposMode())) {
   1690                         MessageFormat subMsgFormat = new MessageFormat(subMsgString, ulocale);
   1691                         subMsgFormat.format(0, null, args, argsMap, dest, null);
   1692                     } else if (dest.attributes == null) {
   1693                         dest.append(subMsgString);
   1694                     } else {
   1695                         // This formats the argument twice, once above to get the subMsgString
   1696                         // and then once more here.
   1697                         // It only happens in formatToCharacterIterator()
   1698                         // on a complex Format set via setFormat(),
   1699                         // and only when the selected subMsgString does not need further formatting.
   1700                         // This imitates ICU 4.6 behavior.
   1701                         dest.formatAndAppend(formatter, arg);
   1702                     }
   1703                 } else {
   1704                     dest.formatAndAppend(formatter, arg);
   1705                 }
   1706             } else if(
   1707                     argType==ArgType.NONE ||
   1708                     (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) {
   1709                 // ArgType.NONE, or
   1710                 // any argument which got reset to null via setFormat() or its siblings.
   1711                 if (arg instanceof Number) {
   1712                     // format number if can
   1713                     dest.formatAndAppend(getStockNumberFormatter(), arg);
   1714                  } else if (arg instanceof Date) {
   1715                     // format a Date if can
   1716                     dest.formatAndAppend(getStockDateFormatter(), arg);
   1717                 } else {
   1718                     dest.append(arg.toString());
   1719                 }
   1720             } else if(argType==ArgType.CHOICE) {
   1721                 if (!(arg instanceof Number)) {
   1722                     throw new IllegalArgumentException("'" + arg + "' is not a Number");
   1723                 }
   1724                 double number = ((Number)arg).doubleValue();
   1725                 int subMsgStart=findChoiceSubMessage(msgPattern, i, number);
   1726                 formatComplexSubMessage(subMsgStart, null, args, argsMap, dest);
   1727             } else if(argType.hasPluralStyle()) {
   1728                 if (!(arg instanceof Number)) {
   1729                     throw new IllegalArgumentException("'" + arg + "' is not a Number");
   1730                 }
   1731                 PluralSelectorProvider selector;
   1732                 if(argType == ArgType.PLURAL) {
   1733                     if (pluralProvider == null) {
   1734                         pluralProvider = new PluralSelectorProvider(this, PluralType.CARDINAL);
   1735                     }
   1736                     selector = pluralProvider;
   1737                 } else {
   1738                     if (ordinalProvider == null) {
   1739                         ordinalProvider = new PluralSelectorProvider(this, PluralType.ORDINAL);
   1740                     }
   1741                     selector = ordinalProvider;
   1742                 }
   1743                 Number number = (Number)arg;
   1744                 double offset=msgPattern.getPluralOffset(i);
   1745                 PluralSelectorContext context =
   1746                         new PluralSelectorContext(i, argName, number, offset);
   1747                 int subMsgStart=PluralFormat.findSubMessage(
   1748                         msgPattern, i, selector, context, number.doubleValue());
   1749                 formatComplexSubMessage(subMsgStart, context, args, argsMap, dest);
   1750             } else if(argType==ArgType.SELECT) {
   1751                 int subMsgStart=SelectFormat.findSubMessage(msgPattern, i, arg.toString());
   1752                 formatComplexSubMessage(subMsgStart, null, args, argsMap, dest);
   1753             } else {
   1754                 // This should never happen.
   1755                 throw new IllegalStateException("unexpected argType "+argType);
   1756             }
   1757             fp = updateMetaData(dest, prevDestLength, fp, argId);
   1758             prevIndex=msgPattern.getPart(argLimit).getLimit();
   1759             i=argLimit;
   1760         }
   1761     }
   1762 
   1763     private void formatComplexSubMessage(
   1764             int msgStart, PluralSelectorContext pluralNumber,
   1765             Object[] args, Map<String, Object> argsMap,
   1766             AppendableWrapper dest) {
   1767         if (!msgPattern.jdkAposMode()) {
   1768             format(msgStart, pluralNumber, args, argsMap, dest, null);
   1769             return;
   1770         }
   1771         // JDK compatibility mode: (see JDK MessageFormat.format() API docs)
   1772         // - remove SKIP_SYNTAX; that is, remove half of the apostrophes
   1773         // - if the result string contains an open curly brace '{' then
   1774         //   instantiate a temporary MessageFormat object and format again;
   1775         //   otherwise just append the result string
   1776         String msgString = msgPattern.getPatternString();
   1777         String subMsgString;
   1778         StringBuilder sb = null;
   1779         int prevIndex = msgPattern.getPart(msgStart).getLimit();
   1780         for (int i = msgStart;;) {
   1781             Part part = msgPattern.getPart(++i);
   1782             Part.Type type = part.getType();
   1783             int index = part.getIndex();
   1784             if (type == Part.Type.MSG_LIMIT) {
   1785                 if (sb == null) {
   1786                     subMsgString = msgString.substring(prevIndex, index);
   1787                 } else {
   1788                     subMsgString = sb.append(msgString, prevIndex, index).toString();
   1789                 }
   1790                 break;
   1791             } else if (type == Part.Type.REPLACE_NUMBER || type == Part.Type.SKIP_SYNTAX) {
   1792                 if (sb == null) {
   1793                     sb = new StringBuilder();
   1794                 }
   1795                 sb.append(msgString, prevIndex, index);
   1796                 if (type == Part.Type.REPLACE_NUMBER) {
   1797                     if(pluralNumber.forReplaceNumber) {
   1798                         // number-offset was already formatted.
   1799                         sb.append(pluralNumber.numberString);
   1800                     } else {
   1801                         sb.append(getStockNumberFormatter().format(pluralNumber.number));
   1802                     }
   1803                 }
   1804                 prevIndex = part.getLimit();
   1805             } else if (type == Part.Type.ARG_START) {
   1806                 if (sb == null) {
   1807                     sb = new StringBuilder();
   1808                 }
   1809                 sb.append(msgString, prevIndex, index);
   1810                 prevIndex = index;
   1811                 i = msgPattern.getLimitPartIndex(i);
   1812                 index = msgPattern.getPart(i).getLimit();
   1813                 MessagePattern.appendReducedApostrophes(msgString, prevIndex, index, sb);
   1814                 prevIndex = index;
   1815             }
   1816         }
   1817         if (subMsgString.indexOf('{') >= 0) {
   1818             MessageFormat subMsgFormat = new MessageFormat("", ulocale);
   1819             subMsgFormat.applyPattern(subMsgString, MessagePattern.ApostropheMode.DOUBLE_REQUIRED);
   1820             subMsgFormat.format(0, null, args, argsMap, dest, null);
   1821         } else {
   1822             dest.append(subMsgString);
   1823         }
   1824     }
   1825 
   1826     /**
   1827      * Read as much literal string from the pattern string as possible. This stops
   1828      * as soon as it finds an argument, or it reaches the end of the string.
   1829      * @param from Index in the pattern string to start from.
   1830      * @return A substring from the pattern string representing the longest possible
   1831      *         substring with no arguments.
   1832      */
   1833     private String getLiteralStringUntilNextArgument(int from) {
   1834         StringBuilder b = new StringBuilder();
   1835         String msgString=msgPattern.getPatternString();
   1836         int prevIndex=msgPattern.getPart(from).getLimit();
   1837         for(int i=from+1;; ++i) {
   1838             Part part=msgPattern.getPart(i);
   1839             Part.Type type=part.getType();
   1840             int index=part.getIndex();
   1841             b.append(msgString, prevIndex, index);
   1842             if(type==Part.Type.ARG_START || type==Part.Type.MSG_LIMIT) {
   1843                 return b.toString();
   1844             }
   1845             assert type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR :
   1846                     "Unexpected Part "+part+" in parsed message.";
   1847             prevIndex=part.getLimit();
   1848         }
   1849     }
   1850 
   1851     private FieldPosition updateMetaData(AppendableWrapper dest, int prevLength,
   1852                                          FieldPosition fp, Object argId) {
   1853         if (dest.attributes != null && prevLength < dest.length) {
   1854             dest.attributes.add(new AttributeAndPosition(argId, prevLength, dest.length));
   1855         }
   1856         if (fp != null && Field.ARGUMENT.equals(fp.getFieldAttribute())) {
   1857             fp.setBeginIndex(prevLength);
   1858             fp.setEndIndex(dest.length);
   1859             return null;
   1860         }
   1861         return fp;
   1862     }
   1863 
   1864     // This lives here because ICU4J does not have its own ChoiceFormat class.
   1865     /**
   1866      * Finds the ChoiceFormat sub-message for the given number.
   1867      * @param pattern A MessagePattern.
   1868      * @param partIndex the index of the first ChoiceFormat argument style part.
   1869      * @param number a number to be mapped to one of the ChoiceFormat argument's intervals
   1870      * @return the sub-message start part index.
   1871      */
   1872     private static int findChoiceSubMessage(MessagePattern pattern, int partIndex, double number) {
   1873         int count=pattern.countParts();
   1874         int msgStart;
   1875         // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples
   1876         // until ARG_LIMIT or end of choice-only pattern.
   1877         // Ignore the first number and selector and start the loop on the first message.
   1878         partIndex+=2;
   1879         for(;;) {
   1880             // Skip but remember the current sub-message.
   1881             msgStart=partIndex;
   1882             partIndex=pattern.getLimitPartIndex(partIndex);
   1883             if(++partIndex>=count) {
   1884                 // Reached the end of the choice-only pattern.
   1885                 // Return with the last sub-message.
   1886                 break;
   1887             }
   1888             Part part=pattern.getPart(partIndex++);
   1889             Part.Type type=part.getType();
   1890             if(type==Part.Type.ARG_LIMIT) {
   1891                 // Reached the end of the ChoiceFormat style.
   1892                 // Return with the last sub-message.
   1893                 break;
   1894             }
   1895             // part is an ARG_INT or ARG_DOUBLE
   1896             assert type.hasNumericValue();
   1897             double boundary=pattern.getNumericValue(part);
   1898             // Fetch the ARG_SELECTOR character.
   1899             int selectorIndex=pattern.getPatternIndex(partIndex++);
   1900             char boundaryChar=pattern.getPatternString().charAt(selectorIndex);
   1901             if(boundaryChar=='<' ? !(number>boundary) : !(number>=boundary)) {
   1902                 // The number is in the interval between the previous boundary and the current one.
   1903                 // Return with the sub-message between them.
   1904                 // The !(a>b) and !(a>=b) comparisons are equivalent to
   1905                 // (a<=b) and (a<b) except they "catch" NaN.
   1906                 break;
   1907             }
   1908         }
   1909         return msgStart;
   1910     }
   1911 
   1912     // Ported from C++ ChoiceFormat::parse().
   1913     private static double parseChoiceArgument(
   1914             MessagePattern pattern, int partIndex,
   1915             String source, ParsePosition pos) {
   1916         // find the best number (defined as the one with the longest parse)
   1917         int start = pos.getIndex();
   1918         int furthest = start;
   1919         double bestNumber = Double.NaN;
   1920         double tempNumber = 0.0;
   1921         while (pattern.getPartType(partIndex) != Part.Type.ARG_LIMIT) {
   1922             tempNumber = pattern.getNumericValue(pattern.getPart(partIndex));
   1923             partIndex += 2;  // skip the numeric part and ignore the ARG_SELECTOR
   1924             int msgLimit = pattern.getLimitPartIndex(partIndex);
   1925             int len = matchStringUntilLimitPart(pattern, partIndex, msgLimit, source, start);
   1926             if (len >= 0) {
   1927                 int newIndex = start + len;
   1928                 if (newIndex > furthest) {
   1929                     furthest = newIndex;
   1930                     bestNumber = tempNumber;
   1931                     if (furthest == source.length()) {
   1932                         break;
   1933                     }
   1934                 }
   1935             }
   1936             partIndex = msgLimit + 1;
   1937         }
   1938         if (furthest == start) {
   1939             pos.setErrorIndex(start);
   1940         } else {
   1941             pos.setIndex(furthest);
   1942         }
   1943         return bestNumber;
   1944     }
   1945 
   1946     /**
   1947      * Matches the pattern string from the end of the partIndex to
   1948      * the beginning of the limitPartIndex,
   1949      * including all syntax except SKIP_SYNTAX,
   1950      * against the source string starting at sourceOffset.
   1951      * If they match, returns the length of the source string match.
   1952      * Otherwise returns -1.
   1953      */
   1954     private static int matchStringUntilLimitPart(
   1955             MessagePattern pattern, int partIndex, int limitPartIndex,
   1956             String source, int sourceOffset) {
   1957         int matchingSourceLength = 0;
   1958         String msgString = pattern.getPatternString();
   1959         int prevIndex = pattern.getPart(partIndex).getLimit();
   1960         for (;;) {
   1961             Part part = pattern.getPart(++partIndex);
   1962             if (partIndex == limitPartIndex || part.getType() == Part.Type.SKIP_SYNTAX) {
   1963                 int index = part.getIndex();
   1964                 int length = index - prevIndex;
   1965                 if (length != 0 && !source.regionMatches(sourceOffset, msgString, prevIndex, length)) {
   1966                     return -1;  // mismatch
   1967                 }
   1968                 matchingSourceLength += length;
   1969                 if (partIndex == limitPartIndex) {
   1970                     return matchingSourceLength;
   1971                 }
   1972                 prevIndex = part.getLimit();  // SKIP_SYNTAX
   1973             }
   1974         }
   1975     }
   1976 
   1977     /**
   1978      * Finds the "other" sub-message.
   1979      * @param partIndex the index of the first PluralFormat argument style part.
   1980      * @return the "other" sub-message start part index.
   1981      */
   1982     private int findOtherSubMessage(int partIndex) {
   1983         int count=msgPattern.countParts();
   1984         MessagePattern.Part part=msgPattern.getPart(partIndex);
   1985         if(part.getType().hasNumericValue()) {
   1986             ++partIndex;
   1987         }
   1988         // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
   1989         // until ARG_LIMIT or end of plural-only pattern.
   1990         do {
   1991             part=msgPattern.getPart(partIndex++);
   1992             MessagePattern.Part.Type type=part.getType();
   1993             if(type==MessagePattern.Part.Type.ARG_LIMIT) {
   1994                 break;
   1995             }
   1996             assert type==MessagePattern.Part.Type.ARG_SELECTOR;
   1997             // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
   1998             if(msgPattern.partSubstringMatches(part, "other")) {
   1999                 return partIndex;
   2000             }
   2001             if(msgPattern.getPartType(partIndex).hasNumericValue()) {
   2002                 ++partIndex;  // skip the numeric-value part of "=1" etc.
   2003             }
   2004             partIndex=msgPattern.getLimitPartIndex(partIndex);
   2005         } while(++partIndex<count);
   2006         return 0;
   2007     }
   2008 
   2009     /**
   2010      * Returns the ARG_START index of the first occurrence of the plural number in a sub-message.
   2011      * Returns -1 if it is a REPLACE_NUMBER.
   2012      * Returns 0 if there is neither.
   2013      */
   2014     private int findFirstPluralNumberArg(int msgStart, String argName) {
   2015         for(int i=msgStart+1;; ++i) {
   2016             Part part=msgPattern.getPart(i);
   2017             Part.Type type=part.getType();
   2018             if(type==Part.Type.MSG_LIMIT) {
   2019                 return 0;
   2020             }
   2021             if(type==Part.Type.REPLACE_NUMBER) {
   2022                 return -1;
   2023             }
   2024             if(type==Part.Type.ARG_START) {
   2025                 ArgType argType=part.getArgType();
   2026                 if(argName.length()!=0 && (argType==ArgType.NONE || argType==ArgType.SIMPLE)) {
   2027                     part=msgPattern.getPart(i+1);  // ARG_NUMBER or ARG_NAME
   2028                     if(msgPattern.partSubstringMatches(part, argName)) {
   2029                         return i;
   2030                     }
   2031                 }
   2032                 i=msgPattern.getLimitPartIndex(i);
   2033             }
   2034         }
   2035     }
   2036 
   2037     /**
   2038      * Mutable input/output values for the PluralSelectorProvider.
   2039      * Separate so that it is possible to make MessageFormat Freezable.
   2040      */
   2041     private static final class PluralSelectorContext {
   2042         private PluralSelectorContext(int start, String name, Number num, double off) {
   2043             startIndex = start;
   2044             argName = name;
   2045             // number needs to be set even when select() is not called.
   2046             // Keep it as a Number/Formattable:
   2047             // For format() methods, and to preserve information (e.g., BigDecimal).
   2048             if(off == 0) {
   2049                 number = num;
   2050             } else {
   2051                 number = num.doubleValue() - off;
   2052             }
   2053             offset = off;
   2054         }
   2055         @Override
   2056         public String toString() {
   2057             throw new AssertionError("PluralSelectorContext being formatted, rather than its number");
   2058         }
   2059 
   2060         // Input values for plural selection with decimals.
   2061         int startIndex;
   2062         String argName;
   2063         /** argument number - plural offset */
   2064         Number number;
   2065         double offset;
   2066         // Output values for plural selection with decimals.
   2067         /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */
   2068         int numberArgIndex;
   2069         Format formatter;
   2070         /** formatted argument number - plural offset */
   2071         String numberString;
   2072         /** true if number-offset was formatted with the stock number formatter */
   2073         boolean forReplaceNumber;
   2074     }
   2075 
   2076     /**
   2077      * This provider helps defer instantiation of a PluralRules object
   2078      * until we actually need to select a keyword.
   2079      * For example, if the number matches an explicit-value selector like "=1"
   2080      * we do not need any PluralRules.
   2081      */
   2082     private static final class PluralSelectorProvider implements PluralFormat.PluralSelector {
   2083         public PluralSelectorProvider(MessageFormat mf, PluralType type) {
   2084             msgFormat = mf;
   2085             this.type = type;
   2086         }
   2087         @Override
   2088         public String select(Object ctx, double number) {
   2089             if(rules == null) {
   2090                 rules = PluralRules.forLocale(msgFormat.ulocale, type);
   2091             }
   2092             // Select a sub-message according to how the number is formatted,
   2093             // which is specified in the selected sub-message.
   2094             // We avoid this circle by looking at how
   2095             // the number is formatted in the "other" sub-message
   2096             // which must always be present and usually contains the number.
   2097             // Message authors should be consistent across sub-messages.
   2098             PluralSelectorContext context = (PluralSelectorContext)ctx;
   2099             int otherIndex = msgFormat.findOtherSubMessage(context.startIndex);
   2100             context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName);
   2101             if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != null) {
   2102                 context.formatter = msgFormat.cachedFormatters.get(context.numberArgIndex);
   2103             }
   2104             if(context.formatter == null) {
   2105                 context.formatter = msgFormat.getStockNumberFormatter();
   2106                 context.forReplaceNumber = true;
   2107             }
   2108             assert context.number.doubleValue() == number;  // argument number minus the offset
   2109             context.numberString = context.formatter.format(context.number);
   2110             if(context.formatter instanceof DecimalFormat) {
   2111                 FixedDecimal dec = ((DecimalFormat)context.formatter).getFixedDecimal(number);
   2112                 return rules.select(dec);
   2113             } else {
   2114                 return rules.select(number);
   2115             }
   2116         }
   2117         private MessageFormat msgFormat;
   2118         private PluralRules rules;
   2119         private PluralType type;
   2120     }
   2121 
   2122     @SuppressWarnings("unchecked")
   2123     private void format(Object arguments, AppendableWrapper result, FieldPosition fp) {
   2124         if ((arguments == null || arguments instanceof Map)) {
   2125             format(null, (Map<String, Object>)arguments, result, fp);
   2126         } else {
   2127             format((Object[])arguments, null, result, fp);
   2128         }
   2129     }
   2130 
   2131     /**
   2132      * Internal routine used by format.
   2133      *
   2134      * @throws IllegalArgumentException if an argument in the
   2135      *         <code>arguments</code> map is not of the type
   2136      *         expected by the format element(s) that use it.
   2137      */
   2138     private void format(Object[] arguments, Map<String, Object> argsMap,
   2139                         AppendableWrapper dest, FieldPosition fp) {
   2140         if (arguments != null && msgPattern.hasNamedArguments()) {
   2141             throw new IllegalArgumentException(
   2142                 "This method is not available in MessageFormat objects " +
   2143                 "that use alphanumeric argument names.");
   2144         }
   2145         format(0, null, arguments, argsMap, dest, fp);
   2146     }
   2147 
   2148     private void resetPattern() {
   2149         if (msgPattern != null) {
   2150             msgPattern.clear();
   2151         }
   2152         if (cachedFormatters != null) {
   2153             cachedFormatters.clear();
   2154         }
   2155         customFormatArgStarts = null;
   2156     }
   2157 
   2158     private static final String[] typeList =
   2159         { "number", "date", "time", "spellout", "ordinal", "duration" };
   2160     private static final int
   2161         TYPE_NUMBER = 0,
   2162         TYPE_DATE = 1,
   2163         TYPE_TIME = 2,
   2164         TYPE_SPELLOUT = 3,
   2165         TYPE_ORDINAL = 4,
   2166         TYPE_DURATION = 5;
   2167 
   2168     private static final String[] modifierList =
   2169         {"", "currency", "percent", "integer"};
   2170 
   2171     private static final int
   2172         MODIFIER_EMPTY = 0,
   2173         MODIFIER_CURRENCY = 1,
   2174         MODIFIER_PERCENT = 2,
   2175         MODIFIER_INTEGER = 3;
   2176 
   2177     private static final String[] dateModifierList =
   2178         {"", "short", "medium", "long", "full"};
   2179 
   2180     private static final int
   2181         DATE_MODIFIER_EMPTY = 0,
   2182         DATE_MODIFIER_SHORT = 1,
   2183         DATE_MODIFIER_MEDIUM = 2,
   2184         DATE_MODIFIER_LONG = 3,
   2185         DATE_MODIFIER_FULL = 4;
   2186 
   2187     // Creates an appropriate Format object for the type and style passed.
   2188     // Both arguments cannot be null.
   2189     private Format createAppropriateFormat(String type, String style) {
   2190         Format newFormat = null;
   2191         int subformatType  = findKeyword(type, typeList);
   2192         switch (subformatType){
   2193         case TYPE_NUMBER:
   2194             switch (findKeyword(style, modifierList)) {
   2195             case MODIFIER_EMPTY:
   2196                 newFormat = NumberFormat.getInstance(ulocale);
   2197                 break;
   2198             case MODIFIER_CURRENCY:
   2199                 newFormat = NumberFormat.getCurrencyInstance(ulocale);
   2200                 break;
   2201             case MODIFIER_PERCENT:
   2202                 newFormat = NumberFormat.getPercentInstance(ulocale);
   2203                 break;
   2204             case MODIFIER_INTEGER:
   2205                 newFormat = NumberFormat.getIntegerInstance(ulocale);
   2206                 break;
   2207             default: // pattern
   2208                 newFormat = new DecimalFormat(style,
   2209                         new DecimalFormatSymbols(ulocale));
   2210                 break;
   2211             }
   2212             break;
   2213         case TYPE_DATE:
   2214             switch (findKeyword(style, dateModifierList)) {
   2215             case DATE_MODIFIER_EMPTY:
   2216                 newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale);
   2217                 break;
   2218             case DATE_MODIFIER_SHORT:
   2219                 newFormat = DateFormat.getDateInstance(DateFormat.SHORT, ulocale);
   2220                 break;
   2221             case DATE_MODIFIER_MEDIUM:
   2222                 newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale);
   2223                 break;
   2224             case DATE_MODIFIER_LONG:
   2225                 newFormat = DateFormat.getDateInstance(DateFormat.LONG, ulocale);
   2226                 break;
   2227             case DATE_MODIFIER_FULL:
   2228                 newFormat = DateFormat.getDateInstance(DateFormat.FULL, ulocale);
   2229                 break;
   2230             default:
   2231                 newFormat = new SimpleDateFormat(style, ulocale);
   2232                 break;
   2233             }
   2234             break;
   2235         case TYPE_TIME:
   2236             switch (findKeyword(style, dateModifierList)) {
   2237             case DATE_MODIFIER_EMPTY:
   2238                 newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale);
   2239                 break;
   2240             case DATE_MODIFIER_SHORT:
   2241                 newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, ulocale);
   2242                 break;
   2243             case DATE_MODIFIER_MEDIUM:
   2244                 newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale);
   2245                 break;
   2246             case DATE_MODIFIER_LONG:
   2247                 newFormat = DateFormat.getTimeInstance(DateFormat.LONG, ulocale);
   2248                 break;
   2249             case DATE_MODIFIER_FULL:
   2250                 newFormat = DateFormat.getTimeInstance(DateFormat.FULL, ulocale);
   2251                 break;
   2252             default:
   2253                 newFormat = new SimpleDateFormat(style, ulocale);
   2254                 break;
   2255             }
   2256             break;
   2257         case TYPE_SPELLOUT:
   2258             {
   2259                 RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale,
   2260                         RuleBasedNumberFormat.SPELLOUT);
   2261                 String ruleset = style.trim();
   2262                 if (ruleset.length() != 0) {
   2263                     try {
   2264                         rbnf.setDefaultRuleSet(ruleset);
   2265                     }
   2266                     catch (Exception e) {
   2267                         // warn invalid ruleset
   2268                     }
   2269                 }
   2270                 newFormat = rbnf;
   2271             }
   2272             break;
   2273         case TYPE_ORDINAL:
   2274             {
   2275                 RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale,
   2276                         RuleBasedNumberFormat.ORDINAL);
   2277                 String ruleset = style.trim();
   2278                 if (ruleset.length() != 0) {
   2279                     try {
   2280                         rbnf.setDefaultRuleSet(ruleset);
   2281                     }
   2282                     catch (Exception e) {
   2283                         // warn invalid ruleset
   2284                     }
   2285                 }
   2286                 newFormat = rbnf;
   2287             }
   2288             break;
   2289         case TYPE_DURATION:
   2290             {
   2291                 RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale,
   2292                         RuleBasedNumberFormat.DURATION);
   2293                 String ruleset = style.trim();
   2294                 if (ruleset.length() != 0) {
   2295                     try {
   2296                         rbnf.setDefaultRuleSet(ruleset);
   2297                     }
   2298                     catch (Exception e) {
   2299                         // warn invalid ruleset
   2300                     }
   2301                 }
   2302                 newFormat = rbnf;
   2303             }
   2304             break;
   2305         default:
   2306             throw new IllegalArgumentException("Unknown format type \"" + type + "\"");
   2307         }
   2308         return newFormat;
   2309     }
   2310 
   2311     private static final Locale rootLocale = new Locale("");  // Locale.ROOT only @since 1.6
   2312 
   2313     private static final int findKeyword(String s, String[] list) {
   2314         s = PatternProps.trimWhiteSpace(s).toLowerCase(rootLocale);
   2315         for (int i = 0; i < list.length; ++i) {
   2316             if (s.equals(list[i]))
   2317                 return i;
   2318         }
   2319         return -1;
   2320     }
   2321 
   2322     /**
   2323      * Custom serialization, new in ICU 4.8.
   2324      * We do not want to use default serialization because we only have a small
   2325      * amount of persistent state which is better expressed explicitly
   2326      * rather than via writing field objects.
   2327      * @param out The output stream.
   2328      * @serialData Writes the locale as a BCP 47 language tag string,
   2329      * the MessagePattern.ApostropheMode as an object,
   2330      * and the pattern string (null if none was applied).
   2331      * Followed by an int with the number of (int formatIndex, Object formatter) pairs,
   2332      * and that many such pairs, corresponding to previous setFormat() calls for custom formats.
   2333      * Followed by an int with the number of (int, Object) pairs,
   2334      * and that many such pairs, for future (post-ICU 4.8) extension of the serialization format.
   2335      */
   2336     private void writeObject(java.io.ObjectOutputStream out) throws IOException {
   2337         out.defaultWriteObject();
   2338         // ICU 4.8 custom serialization.
   2339         // locale as a BCP 47 language tag
   2340         out.writeObject(ulocale.toLanguageTag());
   2341         // ApostropheMode
   2342         if (msgPattern == null) {
   2343             msgPattern = new MessagePattern();
   2344         }
   2345         out.writeObject(msgPattern.getApostropheMode());
   2346         // message pattern string
   2347         out.writeObject(msgPattern.getPatternString());
   2348         // custom formatters
   2349         if (customFormatArgStarts == null || customFormatArgStarts.isEmpty()) {
   2350             out.writeInt(0);
   2351         } else {
   2352             out.writeInt(customFormatArgStarts.size());
   2353             int formatIndex = 0;
   2354             for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
   2355                 if (customFormatArgStarts.contains(partIndex)) {
   2356                     out.writeInt(formatIndex);
   2357                     out.writeObject(cachedFormatters.get(partIndex));
   2358                 }
   2359                 ++formatIndex;
   2360             }
   2361         }
   2362         // number of future (int, Object) pairs
   2363         out.writeInt(0);
   2364     }
   2365 
   2366     /**
   2367      * Custom deserialization, new in ICU 4.8. See comments on writeObject().
   2368      * @throws InvalidObjectException if the objects read from the stream is invalid.
   2369      */
   2370     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
   2371         in.defaultReadObject();
   2372         // ICU 4.8 custom deserialization.
   2373         String languageTag = (String)in.readObject();
   2374         ulocale = ULocale.forLanguageTag(languageTag);
   2375         MessagePattern.ApostropheMode aposMode = (MessagePattern.ApostropheMode)in.readObject();
   2376         if (msgPattern == null || aposMode != msgPattern.getApostropheMode()) {
   2377             msgPattern = new MessagePattern(aposMode);
   2378         }
   2379         String msg = (String)in.readObject();
   2380         if (msg != null) {
   2381             applyPattern(msg);
   2382         }
   2383         // custom formatters
   2384         for (int numFormatters = in.readInt(); numFormatters > 0; --numFormatters) {
   2385             int formatIndex = in.readInt();
   2386             Format formatter = (Format)in.readObject();
   2387             setFormat(formatIndex, formatter);
   2388         }
   2389         // skip future (int, Object) pairs
   2390         for (int numPairs = in.readInt(); numPairs > 0; --numPairs) {
   2391             in.readInt();
   2392             in.readObject();
   2393         }
   2394     }
   2395 
   2396     private void cacheExplicitFormats() {
   2397         if (cachedFormatters != null) {
   2398             cachedFormatters.clear();
   2399         }
   2400         customFormatArgStarts = null;
   2401         // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT
   2402         // which we need not examine.
   2403         int limit = msgPattern.countParts() - 2;
   2404         // This loop starts at part index 1 because we do need to examine
   2405         // ARG_START parts. (But we can ignore the MSG_START.)
   2406         for(int i=1; i < limit; ++i) {
   2407             Part part = msgPattern.getPart(i);
   2408             if(part.getType()!=Part.Type.ARG_START) {
   2409                 continue;
   2410             }
   2411             ArgType argType=part.getArgType();
   2412             if(argType != ArgType.SIMPLE) {
   2413                 continue;
   2414             }
   2415             int index = i;
   2416             i += 2;
   2417             String explicitType = msgPattern.getSubstring(msgPattern.getPart(i++));
   2418             String style = "";
   2419             if ((part = msgPattern.getPart(i)).getType() == MessagePattern.Part.Type.ARG_STYLE) {
   2420                 style = msgPattern.getSubstring(part);
   2421                 ++i;
   2422             }
   2423             Format formatter = createAppropriateFormat(explicitType, style);
   2424             setArgStartFormat(index, formatter);
   2425         }
   2426     }
   2427 
   2428     /**
   2429      * Sets a formatter for a MessagePattern ARG_START part index.
   2430      */
   2431     private void setArgStartFormat(int argStart, Format formatter) {
   2432         if (cachedFormatters == null) {
   2433             cachedFormatters = new HashMap<Integer, Format>();
   2434         }
   2435         cachedFormatters.put(argStart, formatter);
   2436     }
   2437 
   2438     /**
   2439      * Sets a custom formatter for a MessagePattern ARG_START part index.
   2440      * "Custom" formatters are provided by the user via setFormat() or similar APIs.
   2441      */
   2442     private void setCustomArgStartFormat(int argStart, Format formatter) {
   2443         setArgStartFormat(argStart, formatter);
   2444         if (customFormatArgStarts == null) {
   2445             customFormatArgStarts = new HashSet<Integer>();
   2446         }
   2447         customFormatArgStarts.add(argStart);
   2448     }
   2449 
   2450     private static final char SINGLE_QUOTE = '\'';
   2451     private static final char CURLY_BRACE_LEFT = '{';
   2452     private static final char CURLY_BRACE_RIGHT = '}';
   2453 
   2454     private static final int STATE_INITIAL = 0;
   2455     private static final int STATE_SINGLE_QUOTE = 1;
   2456     private static final int STATE_IN_QUOTE = 2;
   2457     private static final int STATE_MSG_ELEMENT = 3;
   2458 
   2459     /**
   2460      * {@icu} Converts an 'apostrophe-friendly' pattern into a standard
   2461      * pattern.
   2462      * <em>This is obsolete for ICU 4.8 and higher MessageFormat pattern strings.</em>
   2463      * It can still be useful together with {@link java.text.MessageFormat}.
   2464      *
   2465      * <p>See the class description for more about apostrophes and quoting,
   2466      * and differences between ICU and {@link java.text.MessageFormat}.
   2467      *
   2468      * <p>{@link java.text.MessageFormat} and ICU 4.6 and earlier MessageFormat
   2469      * treat all ASCII apostrophes as
   2470      * quotes, which is problematic in some languages, e.g.
   2471      * French, where apostrophe is commonly used.  This utility
   2472      * assumes that only an unpaired apostrophe immediately before
   2473      * a brace is a true quote.  Other unpaired apostrophes are paired,
   2474      * and the resulting standard pattern string is returned.
   2475      *
   2476      * <p><b>Note</b>: It is not guaranteed that the returned pattern
   2477      * is indeed a valid pattern.  The only effect is to convert
   2478      * between patterns having different quoting semantics.
   2479      *
   2480      * <p><b>Note</b>: This method only works on top-level messageText,
   2481      * not messageText nested inside a complexArg.
   2482      *
   2483      * @param pattern the 'apostrophe-friendly' pattern to convert
   2484      * @return the standard equivalent of the original pattern
   2485      * @stable ICU 3.4
   2486      */
   2487     public static String autoQuoteApostrophe(String pattern) {
   2488         StringBuilder buf = new StringBuilder(pattern.length() * 2);
   2489         int state = STATE_INITIAL;
   2490         int braceCount = 0;
   2491         for (int i = 0, j = pattern.length(); i < j; ++i) {
   2492             char c = pattern.charAt(i);
   2493             switch (state) {
   2494             case STATE_INITIAL:
   2495                 switch (c) {
   2496                 case SINGLE_QUOTE:
   2497                     state = STATE_SINGLE_QUOTE;
   2498                     break;
   2499                 case CURLY_BRACE_LEFT:
   2500                     state = STATE_MSG_ELEMENT;
   2501                     ++braceCount;
   2502                     break;
   2503                 }
   2504                 break;
   2505             case STATE_SINGLE_QUOTE:
   2506                 switch (c) {
   2507                 case SINGLE_QUOTE:
   2508                     state = STATE_INITIAL;
   2509                     break;
   2510                 case CURLY_BRACE_LEFT:
   2511                 case CURLY_BRACE_RIGHT:
   2512                     state = STATE_IN_QUOTE;
   2513                     break;
   2514                 default:
   2515                     buf.append(SINGLE_QUOTE);
   2516                     state = STATE_INITIAL;
   2517                     break;
   2518                 }
   2519                 break;
   2520             case STATE_IN_QUOTE:
   2521                 switch (c) {
   2522                 case SINGLE_QUOTE:
   2523                     state = STATE_INITIAL;
   2524                     break;
   2525                 }
   2526                 break;
   2527             case STATE_MSG_ELEMENT:
   2528                 switch (c) {
   2529                 case CURLY_BRACE_LEFT:
   2530                     ++braceCount;
   2531                     break;
   2532                 case CURLY_BRACE_RIGHT:
   2533                     if (--braceCount == 0) {
   2534                         state = STATE_INITIAL;
   2535                     }
   2536                     break;
   2537                 }
   2538                 break;
   2539             ///CLOVER:OFF
   2540             default: // Never happens.
   2541                 break;
   2542             ///CLOVER:ON
   2543             }
   2544             buf.append(c);
   2545         }
   2546         // End of scan
   2547         if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) {
   2548             buf.append(SINGLE_QUOTE);
   2549         }
   2550         return new String(buf);
   2551     }
   2552 
   2553     /**
   2554      * Convenience wrapper for Appendable, tracks the result string length.
   2555      * Also, Appendable throws IOException, and we turn that into a RuntimeException
   2556      * so that we need no throws clauses.
   2557      */
   2558     private static final class AppendableWrapper {
   2559         public AppendableWrapper(StringBuilder sb) {
   2560             app = sb;
   2561             length = sb.length();
   2562             attributes = null;
   2563         }
   2564 
   2565         public AppendableWrapper(StringBuffer sb) {
   2566             app = sb;
   2567             length = sb.length();
   2568             attributes = null;
   2569         }
   2570 
   2571         public void useAttributes() {
   2572             attributes = new ArrayList<AttributeAndPosition>();
   2573         }
   2574 
   2575         public void append(CharSequence s) {
   2576             try {
   2577                 app.append(s);
   2578                 length += s.length();
   2579             } catch(IOException e) {
   2580                 throw new ICUUncheckedIOException(e);
   2581             }
   2582         }
   2583 
   2584         public void append(CharSequence s, int start, int limit) {
   2585             try {
   2586                 app.append(s, start, limit);
   2587                 length += limit - start;
   2588             } catch(IOException e) {
   2589                 throw new ICUUncheckedIOException(e);
   2590             }
   2591         }
   2592 
   2593         public void append(CharacterIterator iterator) {
   2594             length += append(app, iterator);
   2595         }
   2596 
   2597         public static int append(Appendable result, CharacterIterator iterator) {
   2598             try {
   2599                 int start = iterator.getBeginIndex();
   2600                 int limit = iterator.getEndIndex();
   2601                 int length = limit - start;
   2602                 if (start < limit) {
   2603                     result.append(iterator.first());
   2604                     while (++start < limit) {
   2605                         result.append(iterator.next());
   2606                     }
   2607                 }
   2608                 return length;
   2609             } catch(IOException e) {
   2610                 throw new ICUUncheckedIOException(e);
   2611             }
   2612         }
   2613 
   2614         public void formatAndAppend(Format formatter, Object arg) {
   2615             if (attributes == null) {
   2616                 append(formatter.format(arg));
   2617             } else {
   2618                 AttributedCharacterIterator formattedArg = formatter.formatToCharacterIterator(arg);
   2619                 int prevLength = length;
   2620                 append(formattedArg);
   2621                 // Copy all of the attributes from formattedArg to our attributes list.
   2622                 formattedArg.first();
   2623                 int start = formattedArg.getIndex();  // Should be 0 but might not be.
   2624                 int limit = formattedArg.getEndIndex();  // == start + length - prevLength
   2625                 int offset = prevLength - start;  // Adjust attribute indexes for the result string.
   2626                 while (start < limit) {
   2627                     Map<Attribute, Object> map = formattedArg.getAttributes();
   2628                     int runLimit = formattedArg.getRunLimit();
   2629                     if (map.size() != 0) {
   2630                         for (Map.Entry<Attribute, Object> entry : map.entrySet()) {
   2631                            attributes.add(
   2632                                new AttributeAndPosition(
   2633                                    entry.getKey(), entry.getValue(),
   2634                                    offset + start, offset + runLimit));
   2635                         }
   2636                     }
   2637                     start = runLimit;
   2638                     formattedArg.setIndex(start);
   2639                 }
   2640             }
   2641         }
   2642 
   2643         public void formatAndAppend(Format formatter, Object arg, String argString) {
   2644             if (attributes == null && argString != null) {
   2645                 append(argString);
   2646             } else {
   2647                 formatAndAppend(formatter, arg);
   2648             }
   2649         }
   2650 
   2651         private Appendable app;
   2652         private int length;
   2653         private List<AttributeAndPosition> attributes;
   2654     }
   2655 
   2656     private static final class AttributeAndPosition {
   2657         /**
   2658          * Defaults the field to Field.ARGUMENT.
   2659          */
   2660         public AttributeAndPosition(Object fieldValue, int startIndex, int limitIndex) {
   2661             init(Field.ARGUMENT, fieldValue, startIndex, limitIndex);
   2662         }
   2663 
   2664         public AttributeAndPosition(Attribute field, Object fieldValue, int startIndex, int limitIndex) {
   2665             init(field, fieldValue, startIndex, limitIndex);
   2666         }
   2667 
   2668         public void init(Attribute field, Object fieldValue, int startIndex, int limitIndex) {
   2669             key = field;
   2670             value = fieldValue;
   2671             start = startIndex;
   2672             limit = limitIndex;
   2673         }
   2674 
   2675         private Attribute key;
   2676         private Object value;
   2677         private int start;
   2678         private int limit;
   2679     }
   2680 }
   2681