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*/
13package com.ibm.icu.text;
14
15import java.io.IOException;
16import java.io.InvalidObjectException;
17import java.io.ObjectInputStream;
18import java.text.AttributedCharacterIterator;
19import java.text.AttributedCharacterIterator.Attribute;
20import java.text.AttributedString;
21import java.text.CharacterIterator;
22import java.text.ChoiceFormat;
23import java.text.FieldPosition;
24import java.text.Format;
25import java.text.ParseException;
26import java.text.ParsePosition;
27import java.util.ArrayList;
28import java.util.Date;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Locale;
34import java.util.Map;
35import java.util.Set;
36
37import com.ibm.icu.impl.PatternProps;
38import com.ibm.icu.impl.Utility;
39import com.ibm.icu.text.MessagePattern.ArgType;
40import com.ibm.icu.text.MessagePattern.Part;
41import com.ibm.icu.text.PluralRules.FixedDecimal;
42import com.ibm.icu.text.PluralRules.PluralType;
43import com.ibm.icu.util.ICUUncheckedIOException;
44import com.ibm.icu.util.ULocale;
45import 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 */
335public 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