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