KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > text > MessageFormat


1 /*
2  * @(#)MessageFormat.java 1.56 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 /*
9  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
10  * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
11  *
12  * The original version of this source code and documentation is copyrighted
13  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
14  * materials are provided under terms of a License Agreement between Taligent
15  * and Sun. This technology is protected by multiple US and International
16  * patents. This notice and attribution to Taligent may not be removed.
17  * Taligent is a registered trademark of Taligent, Inc.
18  *
19  */

20
21 package java.text;
22
23 import java.io.InvalidObjectException JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.ObjectInputStream JavaDoc;
26 import java.text.DecimalFormat JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Locale JavaDoc;
31 import sun.text.Utility;
32
33
34 /**
35  * <code>MessageFormat</code> provides a means to produce concatenated
36  * messages in language-neutral way. Use this to construct messages
37  * displayed for end users.
38  *
39  * <p>
40  * <code>MessageFormat</code> takes a set of objects, formats them, then
41  * inserts the formatted strings into the pattern at the appropriate places.
42  *
43  * <p>
44  * <strong>Note:</strong>
45  * <code>MessageFormat</code> differs from the other <code>Format</code>
46  * classes in that you create a <code>MessageFormat</code> object with one
47  * of its constructors (not with a <code>getInstance</code> style factory
48  * method). The factory methods aren't necessary because <code>MessageFormat</code>
49  * itself doesn't implement locale specific behavior. Any locale specific
50  * behavior is defined by the pattern that you provide as well as the
51  * subformats used for inserted arguments.
52  *
53  * <h4><a name="patterns">Patterns and Their Interpretation</a></h4>
54  *
55  * <code>MessageFormat</code> uses patterns of the following form:
56  * <blockquote><pre>
57  * <i>MessageFormatPattern:</i>
58  * <i>String</i>
59  * <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i>
60  *
61  * <i>FormatElement:</i>
62  * { <i>ArgumentIndex</i> }
63  * { <i>ArgumentIndex</i> , <i>FormatType</i> }
64  * { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> }
65  *
66  * <i>FormatType: one of </i>
67  * number date time choice
68  *
69  * <i>FormatStyle:</i>
70  * short
71  * medium
72  * long
73  * full
74  * integer
75  * currency
76  * percent
77  * <i>SubformatPattern</i>
78  *
79  * <i>String:</i>
80  * <i>StringPart<sub>opt</sub></i>
81  * <i>String</i> <i>StringPart</i>
82  *
83  * <i>StringPart:</i>
84  * ''
85  * ' <i>QuotedString</i> '
86  * <i>UnquotedString</i>
87  *
88  * <i>SubformatPattern:</i>
89  * <i>SubformatPatternPart<sub>opt</sub></i>
90  * <i>SubformatPattern</i> <i>SubformatPatternPart</i>
91  *
92  * <i>SubFormatPatternPart:</i>
93  * ' <i>QuotedPattern</i> '
94  * <i>UnquotedPattern</i>
95  * </pre></blockquote>
96  *
97  * <p>
98  * Within a <i>String</i>, <code>"''"</code> represents a single
99  * quote. A <i>QuotedString</i> can contain arbitrary characters
100  * except single quotes; the surrounding single quotes are removed.
101  * An <i>UnquotedString</i> can contain arbitrary characters
102  * except single quotes and left curly brackets. Thus, a string that
103  * should result in the formatted message "'{0}'" can be written as
104  * <code>"'''{'0}''"</code> or <code>"'''{0}'''"</code>.
105  * <p>
106  * Within a <i>SubformatPattern</i>, different rules apply.
107  * A <i>QuotedPattern</i> can contain arbitrary characters
108  * except single quotes; but the surrounding single quotes are
109  * <strong>not</strong> removed, so they may be interpreted by the
110  * subformat. For example, <code>"{1,number,$'#',##}"</code> will
111  * produce a number format with the pound-sign quoted, with a result
112  * such as: "$#31,45".
113  * An <i>UnquotedPattern</i> can contain arbitrary characters
114  * except single quotes, but curly braces within it must be balanced.
115  * For example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code>
116  * are valid subformat patterns, but <code>"ab {0'}' de"</code> and
117  * <code>"ab } de"</code> are not.
118  * <p>
119  * <dl><dt><b>Warning:</b><dd>The rules for using quotes within message
120  * format patterns unfortunately have shown to be somewhat confusing.
121  * In particular, it isn't always obvious to localizers whether single
122  * quotes need to be doubled or not. Make sure to inform localizers about
123  * the rules, and tell them (for example, by using comments in resource
124  * bundle source files) which strings will be processed by MessageFormat.
125  * Note that localizers may need to use single quotes in translated
126  * strings where the original version doesn't have them.
127  * </dl>
128  * <p>
129  * The <i>ArgumentIndex</i> value is a non-negative integer written
130  * using the digits '0' through '9', and represents an index into the
131  * <code>arguments</code> array passed to the <code>format</code> methods
132  * or the result array returned by the <code>parse</code> methods.
133  * <p>
134  * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create
135  * a <code>Format</code> instance for the format element. The following
136  * table shows how the values map to Format instances. Combinations not
137  * shown in the table are illegal. A <i>SubformatPattern</i> must
138  * be a valid pattern string for the Format subclass used.
139  * <p>
140  * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances">
141  * <tr>
142  * <th id="ft">Format Type
143  * <th id="fs">Format Style
144  * <th id="sc">Subformat Created
145  * <tr>
146  * <td headers="ft"><i>(none)</i>
147  * <td headers="fs"><i>(none)</i>
148  * <td headers="sc"><code>null</code>
149  * <tr>
150  * <td headers="ft" rowspan=5><code>number</code>
151  * <td headers="fs"><i>(none)</i>
152  * <td headers="sc"><code>NumberFormat.getInstance(getLocale())</code>
153  * <tr>
154  * <td headers="fs"><code>integer</code>
155  * <td headers="sc"><code>NumberFormat.getIntegerInstance(getLocale())</code>
156  * <tr>
157  * <td headers="fs"><code>currency</code>
158  * <td headers="sc"><code>NumberFormat.getCurrencyInstance(getLocale())</code>
159  * <tr>
160  * <td headers="fs"><code>percent</code>
161  * <td headers="sc"><code>NumberFormat.getPercentInstance(getLocale())</code>
162  * <tr>
163  * <td headers="fs"><i>SubformatPattern</i>
164  * <td headers="sc"><code>new DecimalFormat(subformatPattern, new DecimalFormatSymbols(getLocale()))</code>
165  * <tr>
166  * <td headers="ft" rowspan=6><code>date</code>
167  * <td headers="fs"><i>(none)</i>
168  * <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
169  * <tr>
170  * <td headers="fs"><code>short</code>
171  * <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code>
172  * <tr>
173  * <td headers="fs"><code>medium</code>
174  * <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
175  * <tr>
176  * <td headers="fs"><code>long</code>
177  * <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code>
178  * <tr>
179  * <td headers="fs"><code>full</code>
180  * <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code>
181  * <tr>
182  * <td headers="fs"><i>SubformatPattern</i>
183  * <td headers="sc"><code>new SimpleDateFormat(subformatPattern, getLocale())
184  * <tr>
185  * <td headers="ft" rowspan=6><code>time</code>
186  * <td headers="fs"><i>(none)</i>
187  * <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
188  * <tr>
189  * <td headers="fs"><code>short</code>
190  * <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code>
191  * <tr>
192  * <td headers="fs"><code>medium</code>
193  * <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
194  * <tr>
195  * <td headers="fs"><code>long</code>
196  * <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code>
197  * <tr>
198  * <td headers="fs"><code>full</code>
199  * <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code>
200  * <tr>
201  * <td headers="fs"><i>SubformatPattern</i>
202  * <td headers="sc"><code>new SimpleDateFormat(subformatPattern, getLocale())
203  * <tr>
204  * <td headers="ft"><code>choice</code>
205  * <td headers="fs"><i>SubformatPattern</i>
206  * <td headers="sc"><code>new ChoiceFormat(subformatPattern)</code>
207  * </table>
208  * <p>
209  *
210  * <h4>Usage Information</h4>
211  *
212  * <p>
213  * Here are some examples of usage.
214  * In real internationalized programs, the message format pattern and other
215  * static strings will, of course, be obtained from resource bundles.
216  * Other parameters will be dynamically determined at runtime.
217  * <p>
218  * The first example uses the static method <code>MessageFormat.format</code>,
219  * which internally creates a <code>MessageFormat</code> for one-time use:
220  * <blockquote><pre>
221  * int planet = 7;
222  * String event = "a disturbance in the Force";
223  *
224  * String result = MessageFormat.format(
225  * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
226  * planet, new Date(), event);
227  * </pre></blockquote>
228  * The output is:
229  * <blockquote><pre>
230  * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
231  * </pre></blockquote>
232  *
233  * <p>
234  * The following example creates a <code>MessageFormat</code> instance that
235  * can be used repeatedly:
236  * <blockquote><pre>
237  * int fileCount = 1273;
238  * String diskName = "MyDisk";
239  * Object[] testArgs = {new Long(fileCount), diskName};
240  *
241  * MessageFormat form = new MessageFormat(
242  * "The disk \"{1}\" contains {0} file(s).");
243  *
244  * System.out.println(form.format(testArgs));
245  * </pre></blockquote>
246  * The output with different values for <code>fileCount</code>:
247  * <blockquote><pre>
248  * The disk "MyDisk" contains 0 file(s).
249  * The disk "MyDisk" contains 1 file(s).
250  * The disk "MyDisk" contains 1,273 file(s).
251  * </pre></blockquote>
252  *
253  * <p>
254  * For more sophisticated patterns, you can use a <code>ChoiceFormat</code>
255  * to produce correct forms for singular and plural:
256  * <blockquote><pre>
257  * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
258  * double[] filelimits = {0,1,2};
259  * String[] filepart = {"no files","one file","{0,number} files"};
260  * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
261  * form.setFormatByArgumentIndex(0, fileform);
262  *
263  * int fileCount = 1273;
264  * String diskName = "MyDisk";
265  * Object[] testArgs = {new Long(fileCount), diskName};
266  *
267  * System.out.println(form.format(testArgs));
268  * </pre></blockquote>
269  * The output with different values for <code>fileCount</code>:
270  * <blockquote><pre>
271  * The disk "MyDisk" contains no files.
272  * The disk "MyDisk" contains one file.
273  * The disk "MyDisk" contains 1,273 files.
274  * </pre></blockquote>
275  *
276  * <p>
277  * You can create the <code>ChoiceFormat</code> programmatically, as in the
278  * above example, or by using a pattern. See {@link ChoiceFormat}
279  * for more information.
280  * <blockquote><pre>
281  * form.applyPattern(
282  * "There {0,choice,0#are no files|1#is one file|1&lt;are {0,number,integer} files}.");
283  * </pre></blockquote>
284  *
285  * <p>
286  * <strong>Note:</strong> As we see above, the string produced
287  * by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated specially;
288  * occurences of '{' are used to indicated subformats, and cause recursion.
289  * If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code>
290  * programmatically (instead of using the string patterns), then be careful not to
291  * produce a format that recurses on itself, which will cause an infinite loop.
292  * <p>
293  * When a single argument is parsed more than once in the string, the last match
294  * will be the final result of the parsing. For example,
295  * <blockquote><pre>
296  * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
297  * Object[] objs = {new Double(3.1415)};
298  * String result = mf.format( objs );
299  * // result now equals "3.14, 3.1"
300  * objs = null;
301  * objs = mf.parse(result, new ParsePosition(0));
302  * // objs now equals {new Double(3.1)}
303  * </pre></blockquote>
304  *
305  * <p>
306  * Likewise, parsing with a MessageFormat object using patterns containing
307  * multiple occurences of the same argument would return the last match. For
308  * example,
309  * <blockquote><pre>
310  * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
311  * String forParsing = "x, y, z";
312  * Object[] objs = mf.parse(forParsing, new ParsePosition(0));
313  * // result now equals {new String("z")}
314  * </pre></blockquote>
315  *
316  * <h4><a name="synchronization">Synchronization</a></h4>
317  *
318  * <p>
319  * Message formats are not synchronized.
320  * It is recommended to create separate format instances for each thread.
321  * If multiple threads access a format concurrently, it must be synchronized
322  * externally.
323  *
324  * @see java.util.Locale
325  * @see Format
326  * @see NumberFormat
327  * @see DecimalFormat
328  * @see ChoiceFormat
329  * @version 1.56, 12/19/03
330  * @author Mark Davis
331  */

332
333 public class MessageFormat extends Format JavaDoc {
334
335     private static final long serialVersionUID = 6479157306784022952L;
336
337     /**
338      * Constructs a MessageFormat for the default locale and the
339      * specified pattern.
340      * The constructor first sets the locale, then parses the pattern and
341      * creates a list of subformats for the format elements contained in it.
342      * Patterns and their interpretation are specified in the
343      * <a HREF="#patterns">class description</a>.
344      *
345      * @param pattern the pattern for this message format
346      * @exception IllegalArgumentException if the pattern is invalid
347      */

348     public MessageFormat(String JavaDoc pattern) {
349         this.locale = Locale.getDefault();
350         applyPattern(pattern);
351     }
352
353     /**
354      * Constructs a MessageFormat for the specified locale and
355      * pattern.
356      * The constructor first sets the locale, then parses the pattern and
357      * creates a list of subformats for the format elements contained in it.
358      * Patterns and their interpretation are specified in the
359      * <a HREF="#patterns">class description</a>.
360      *
361      * @param pattern the pattern for this message format
362      * @param locale the locale for this message format
363      * @exception IllegalArgumentException if the pattern is invalid
364      * @since 1.4
365      */

366     public MessageFormat(String JavaDoc pattern, Locale JavaDoc locale) {
367         this.locale = locale;
368         applyPattern(pattern);
369     }
370
371     /**
372      * Sets the locale to be used when creating or comparing subformats.
373      * This affects subsequent calls to the {@link #applyPattern applyPattern}
374      * and {@link #toPattern toPattern} methods as well as to the
375      * <code>format</code> and
376      * {@link #formatToCharacterIterator formatToCharacterIterator} methods.
377      *
378      * @param locale the locale to be used when creating or comparing subformats
379      */

380     public void setLocale(Locale JavaDoc locale) {
381         this.locale = locale;
382     }
383
384     /**
385      * Gets the locale that's used when creating or comparing subformats.
386      *
387      * @return the locale used when creating or comparing subformats
388      */

389     public Locale JavaDoc getLocale() {
390         return locale;
391     }
392
393
394     /**
395      * Sets the pattern used by this message format.
396      * The method parses the pattern and creates a list of subformats
397      * for the format elements contained in it.
398      * Patterns and their interpretation are specified in the
399      * <a HREF="#patterns">class description</a>.
400      *
401      * @param pattern the pattern for this message format
402      * @exception IllegalArgumentException if the pattern is invalid
403      */

404     public void applyPattern(String JavaDoc pattern) {
405             StringBuffer JavaDoc[] segments = new StringBuffer JavaDoc[4];
406             for (int i = 0; i < segments.length; ++i) {
407                 segments[i] = new StringBuffer JavaDoc();
408             }
409             int part = 0;
410             int formatNumber = 0;
411             boolean inQuote = false;
412             int braceStack = 0;
413             maxOffset = -1;
414             for (int i = 0; i < pattern.length(); ++i) {
415                 char ch = pattern.charAt(i);
416                 if (part == 0) {
417                     if (ch == '\'') {
418                         if (i + 1 < pattern.length()
419                             && pattern.charAt(i+1) == '\'') {
420                             segments[part].append(ch); // handle doubles
421
++i;
422                         } else {
423                             inQuote = !inQuote;
424                         }
425                     } else if (ch == '{' && !inQuote) {
426                         part = 1;
427                     } else {
428                         segments[part].append(ch);
429                     }
430                 } else if (inQuote) { // just copy quotes in parts
431
segments[part].append(ch);
432                     if (ch == '\'') {
433                         inQuote = false;
434                     }
435                 } else {
436                     switch (ch) {
437                     case ',':
438                         if (part < 3)
439                             part += 1;
440                         else
441                             segments[part].append(ch);
442                         break;
443                     case '{':
444                         ++braceStack;
445                         segments[part].append(ch);
446                         break;
447                     case '}':
448                         if (braceStack == 0) {
449                             part = 0;
450                             makeFormat(i, formatNumber, segments);
451                             formatNumber++;
452                         } else {
453                             --braceStack;
454                             segments[part].append(ch);
455                         }
456                         break;
457                     case '\'':
458                         inQuote = true;
459                         // fall through, so we keep quotes in other parts
460
default:
461                         segments[part].append(ch);
462                         break;
463                     }
464                 }
465             }
466             if (braceStack == 0 && part != 0) {
467                 maxOffset = -1;
468                 throw new IllegalArgumentException JavaDoc("Unmatched braces in the pattern.");
469             }
470             this.pattern = segments[0].toString();
471     }
472
473
474     /**
475      * Returns a pattern representing the current state of the message format.
476      * The string is constructed from internal information and therefore
477      * does not necessarily equal the previously applied pattern.
478      *
479      * @return a pattern representing the current state of the message format
480      */

481     public String JavaDoc toPattern() {
482         // later, make this more extensible
483
int lastOffset = 0;
484         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
485         for (int i = 0; i <= maxOffset; ++i) {
486             copyAndFixQuotes(pattern, lastOffset, offsets[i],result);
487             lastOffset = offsets[i];
488             result.append('{');
489             result.append(argumentNumbers[i]);
490             if (formats[i] == null) {
491                 // do nothing, string format
492
} else if (formats[i] instanceof DecimalFormat JavaDoc) {
493                 if (formats[i].equals(NumberFormat.getInstance(locale))) {
494                     result.append(",number");
495                 } else if (formats[i].equals(NumberFormat.getCurrencyInstance(locale))) {
496                     result.append(",number,currency");
497                 } else if (formats[i].equals(NumberFormat.getPercentInstance(locale))) {
498                     result.append(",number,percent");
499                 } else if (formats[i].equals(NumberFormat.getIntegerInstance(locale))) {
500                     result.append(",number,integer");
501                 } else {
502                     result.append(",number," +
503                                   ((DecimalFormat JavaDoc)formats[i]).toPattern());
504                 }
505             } else if (formats[i] instanceof SimpleDateFormat JavaDoc) {
506                 if (formats[i].equals(DateFormat.getDateInstance(
507                                                                DateFormat.DEFAULT,locale))) {
508                     result.append(",date");
509                 } else if (formats[i].equals(DateFormat.getDateInstance(
510                                                                       DateFormat.SHORT,locale))) {
511                     result.append(",date,short");
512                 } else if (formats[i].equals(DateFormat.getDateInstance(
513                                                                       DateFormat.DEFAULT,locale))) {
514                     result.append(",date,medium");
515                 } else if (formats[i].equals(DateFormat.getDateInstance(
516                                                                       DateFormat.LONG,locale))) {
517                     result.append(",date,long");
518                 } else if (formats[i].equals(DateFormat.getDateInstance(
519                                                                       DateFormat.FULL,locale))) {
520                     result.append(",date,full");
521                 } else if (formats[i].equals(DateFormat.getTimeInstance(
522                                                                       DateFormat.DEFAULT,locale))) {
523                     result.append(",time");
524                 } else if (formats[i].equals(DateFormat.getTimeInstance(
525                                                                       DateFormat.SHORT,locale))) {
526                     result.append(",time,short");
527                 } else if (formats[i].equals(DateFormat.getTimeInstance(
528                                                                       DateFormat.DEFAULT,locale))) {
529                     result.append(",time,medium");
530                 } else if (formats[i].equals(DateFormat.getTimeInstance(
531                                                                       DateFormat.LONG,locale))) {
532                     result.append(",time,long");
533                 } else if (formats[i].equals(DateFormat.getTimeInstance(
534                                                                       DateFormat.FULL,locale))) {
535                     result.append(",time,full");
536                 } else {
537                     result.append(",date,"
538                                   + ((SimpleDateFormat JavaDoc)formats[i]).toPattern());
539                 }
540             } else if (formats[i] instanceof ChoiceFormat JavaDoc) {
541                 result.append(",choice,"
542                               + ((ChoiceFormat JavaDoc)formats[i]).toPattern());
543             } else {
544                 //result.append(", unknown");
545
}
546             result.append('}');
547         }
548         copyAndFixQuotes(pattern, lastOffset, pattern.length(), result);
549         return result.toString();
550     }
551
552     /**
553      * Sets the formats to use for the values passed into
554      * <code>format</code> methods or returned from <code>parse</code>
555      * methods. The indices of elements in <code>newFormats</code>
556      * correspond to the argument indices used in the previously set
557      * pattern string.
558      * The order of formats in <code>newFormats</code> thus corresponds to
559      * the order of elements in the <code>arguments</code> array passed
560      * to the <code>format</code> methods or the result array returned
561      * by the <code>parse</code> methods.
562      * <p>
563      * If an argument index is used for more than one format element
564      * in the pattern string, then the corresponding new format is used
565      * for all such format elements. If an argument index is not used
566      * for any format element in the pattern string, then the
567      * corresponding new format is ignored. If fewer formats are provided
568      * than needed, then only the formats for argument indices less
569      * than <code>newFormats.length</code> are replaced.
570      *
571      * @param newFormats the new formats to use
572      * @exception NullPointerException if <code>newFormats</code> is null
573      * @since 1.4
574      */

575     public void setFormatsByArgumentIndex(Format JavaDoc[] newFormats) {
576         for (int i = 0; i <= maxOffset; i++) {
577             int j = argumentNumbers[i];
578             if (j < newFormats.length) {
579                 formats[i] = newFormats[j];
580             }
581         }
582     }
583
584     /**
585      * Sets the formats to use for the format elements in the
586      * previously set pattern string.
587      * The order of formats in <code>newFormats</code> corresponds to
588      * the order of format elements in the pattern string.
589      * <p>
590      * If more formats are provided than needed by the pattern string,
591      * the remaining ones are ignored. If fewer formats are provided
592      * than needed, then only the first <code>newFormats.length</code>
593      * formats are replaced.
594      * <p>
595      * Since the order of format elements in a pattern string often
596      * changes during localization, it is generally better to use the
597      * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}
598      * method, which assumes an order of formats corresponding to the
599      * order of elements in the <code>arguments</code> array passed to
600      * the <code>format</code> methods or the result array returned by
601      * the <code>parse</code> methods.
602      *
603      * @param newFormats the new formats to use
604      * @exception NullPointerException if <code>newFormats</code> is null
605      */

606     public void setFormats(Format JavaDoc[] newFormats) {
607         int runsToCopy = newFormats.length;
608         if (runsToCopy > maxOffset + 1) {
609             runsToCopy = maxOffset + 1;
610         }
611         for (int i = 0; i < runsToCopy; i++) {
612             formats[i] = newFormats[i];
613         }
614     }
615
616     /**
617      * Sets the format to use for the format elements within the
618      * previously set pattern string that use the given argument
619      * index.
620      * The argument index is part of the format element definition and
621      * represents an index into the <code>arguments</code> array passed
622      * to the <code>format</code> methods or the result array returned
623      * by the <code>parse</code> methods.
624      * <p>
625      * If the argument index is used for more than one format element
626      * in the pattern string, then the new format is used for all such
627      * format elements. If the argument index is not used for any format
628      * element in the pattern string, then the new format is ignored.
629      *
630      * @param argumentIndex the argument index for which to use the new format
631      * @param newFormat the new format to use
632      * @since 1.4
633      */

634     public void setFormatByArgumentIndex(int argumentIndex, Format JavaDoc newFormat) {
635         for (int j = 0; j <= maxOffset; j++) {
636             if (argumentNumbers[j] == argumentIndex) {
637                 formats[j] = newFormat;
638             }
639         }
640     }
641
642     /**
643      * Sets the format to use for the format element with the given
644      * format element index within the previously set pattern string.
645      * The format element index is the zero-based number of the format
646      * element counting from the start of the pattern string.
647      * <p>
648      * Since the order of format elements in a pattern string often
649      * changes during localization, it is generally better to use the
650      * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}
651      * method, which accesses format elements based on the argument
652      * index they specify.
653      *
654      * @param formatElementIndex the index of a format element within the pattern
655      * @param newFormat the format to use for the specified format element
656      * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or
657      * larger than the number of format elements in the pattern string
658      */

659     public void setFormat(int formatElementIndex, Format JavaDoc newFormat) {
660         formats[formatElementIndex] = newFormat;
661     }
662
663     /**
664      * Gets the formats used for the values passed into
665      * <code>format</code> methods or returned from <code>parse</code>
666      * methods. The indices of elements in the returned array
667      * correspond to the argument indices used in the previously set
668      * pattern string.
669      * The order of formats in the returned array thus corresponds to
670      * the order of elements in 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 an argument index is used for more than one format element
675      * in the pattern string, then the format used for the last such
676      * format element is returned in the array. If an argument index
677      * is not used for any format element in the pattern string, then
678      * null is returned in the array.
679      *
680      * @return the formats used for the arguments within the pattern
681      * @since 1.4
682      */

683     public Format JavaDoc[] getFormatsByArgumentIndex() {
684         int maximumArgumentNumber = -1;
685         for (int i = 0; i <= maxOffset; i++) {
686             if (argumentNumbers[i] > maximumArgumentNumber) {
687                 maximumArgumentNumber = argumentNumbers[i];
688             }
689         }
690         Format JavaDoc[] resultArray = new Format JavaDoc[maximumArgumentNumber + 1];
691         for (int i = 0; i <= maxOffset; i++) {
692             resultArray[argumentNumbers[i]] = formats[i];
693         }
694         return resultArray;
695     }
696
697     /**
698      * Gets the formats used for the format elements in the
699      * previously set pattern string.
700      * The order of formats in the returned array corresponds to
701      * the order of format elements in the pattern string.
702      * <p>
703      * Since the order of format elements in a pattern string often
704      * changes during localization, it's generally better to use the
705      * {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex}
706      * method, which assumes an order of formats corresponding to the
707      * order of elements in the <code>arguments</code> array passed to
708      * the <code>format</code> methods or the result array returned by
709      * the <code>parse</code> methods.
710      *
711      * @return the formats used for the format elements in the pattern
712      */

713     public Format JavaDoc[] getFormats() {
714         Format JavaDoc[] resultArray = new Format JavaDoc[maxOffset + 1];
715         System.arraycopy(formats, 0, resultArray, 0, maxOffset + 1);
716         return resultArray;
717     }
718
719     /**
720      * Formats an array of objects and appends the <code>MessageFormat</code>'s
721      * pattern, with format elements replaced by the formatted objects, to the
722      * provided <code>StringBuffer</code>.
723      * <p>
724      * The text substituted for the individual format elements is derived from
725      * the current subformat of the format element and the
726      * <code>arguments</code> element at the format element's argument index
727      * as indicated by the first matching line of the following table. An
728      * argument is <i>unavailable</i> if <code>arguments</code> is
729      * <code>null</code> or has fewer than argumentIndex+1 elements.
730      * <p>
731      * <table border=1 summary="Examples of subformat,argument,and formatted text">
732      * <tr>
733      * <th>Subformat
734      * <th>Argument
735      * <th>Formatted Text
736      * <tr>
737      * <td><i>any</i>
738      * <td><i>unavailable</i>
739      * <td><code>"{" + argumentIndex + "}"</code>
740      * <tr>
741      * <td><i>any</i>
742      * <td><code>null</code>
743      * <td><code>"null"</code>
744      * <tr>
745      * <td><code>instanceof ChoiceFormat</code>
746      * <td><i>any</i>
747      * <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>
748      * (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :
749      * subformat.format(argument)</code>
750      * <tr>
751      * <td><code>!= null</code>
752      * <td><i>any</i>
753      * <td><code>subformat.format(argument)</code>
754      * <tr>
755      * <td><code>null</code>
756      * <td><code>instanceof Number</code>
757      * <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
758      * <tr>
759      * <td><code>null</code>
760      * <td><code>instanceof Date</code>
761      * <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>
762      * <tr>
763      * <td><code>null</code>
764      * <td><code>instanceof String</code>
765      * <td><code>argument</code>
766      * <tr>
767      * <td><code>null</code>
768      * <td><i>any</i>
769      * <td><code>argument.toString()</code>
770      * </table>
771      * <p>
772      * If <code>pos</code> is non-null, and refers to
773      * <code>Field.ARGUMENT</code>, the location of the first formatted
774      * string will be returned.
775      *
776      * @param arguments an array of objects to be formatted and substituted.
777      * @param result where text is appended.
778      * @param pos On input: an alignment field, if desired.
779      * On output: the offsets of the alignment field.
780      * @exception IllegalArgumentException if an argument in the
781      * <code>arguments</code> array is not of the type
782      * expected by the format element(s) that use it.
783      */

784     public final StringBuffer JavaDoc format(Object JavaDoc[] arguments, StringBuffer JavaDoc result,
785                                      FieldPosition JavaDoc pos)
786     {
787         return subformat(arguments, result, pos, null);
788     }
789
790     /**
791      * Creates a MessageFormat with the given pattern and uses it
792      * to format the given arguments. This is equivalent to
793      * <blockquote>
794      * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
795      * </blockquote>
796      *
797      * @exception IllegalArgumentException if the pattern is invalid,
798      * or if an argument in the <code>arguments</code> array
799      * is not of the type expected by the format element(s)
800      * that use it.
801      */

802     public static String JavaDoc format(String JavaDoc pattern, Object JavaDoc ... arguments) {
803         MessageFormat JavaDoc temp = new MessageFormat JavaDoc(pattern);
804         return temp.format(arguments);
805     }
806
807     // Overrides
808
/**
809      * Formats an array of objects and appends the <code>MessageFormat</code>'s
810      * pattern, with format elements replaced by the formatted objects, to the
811      * provided <code>StringBuffer</code>.
812      * This is equivalent to
813      * <blockquote>
814      * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
815      * </blockquote>
816      *
817      * @param arguments an array of objects to be formatted and substituted.
818      * @param result where text is appended.
819      * @param pos On input: an alignment field, if desired.
820      * On output: the offsets of the alignment field.
821      * @exception IllegalArgumentException if an argument in the
822      * <code>arguments</code> array is not of the type
823      * expected by the format element(s) that use it.
824      */

825     public final StringBuffer JavaDoc format(Object JavaDoc arguments, StringBuffer JavaDoc result,
826                                      FieldPosition JavaDoc pos)
827     {
828         return subformat((Object JavaDoc[]) arguments, result, pos, null);
829     }
830
831     /**
832      * Formats an array of objects and inserts them into the
833      * <code>MessageFormat</code>'s pattern, producing an
834      * <code>AttributedCharacterIterator</code>.
835      * You can use the returned <code>AttributedCharacterIterator</code>
836      * to build the resulting String, as well as to determine information
837      * about the resulting String.
838      * <p>
839      * The text of the returned <code>AttributedCharacterIterator</code> is
840      * the same that would be returned by
841      * <blockquote>
842      * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
843      * </blockquote>
844      * <p>
845      * In addition, the <code>AttributedCharacterIterator</code> contains at
846      * least attributes indicating where text was generated from an
847      * argument in the <code>arguments</code> array. The keys of these attributes are of
848      * type <code>MessageFormat.Field</code>, their values are
849      * <code>Integer</code> objects indicating the index in the <code>arguments</code>
850      * array of the argument from which the text was generated.
851      * <p>
852      * The attributes/value from the underlying <code>Format</code>
853      * instances that <code>MessageFormat</code> uses will also be
854      * placed in the resulting <code>AttributedCharacterIterator</code>.
855      * This allows you to not only find where an argument is placed in the
856      * resulting String, but also which fields it contains in turn.
857      *
858      * @param arguments an array of objects to be formatted and substituted.
859      * @return AttributedCharacterIterator describing the formatted value.
860      * @exception NullPointerException if <code>arguments</code> is null.
861      * @exception IllegalArgumentException if an argument in the
862      * <code>arguments</code> array is not of the type
863      * expected by the format element(s) that use it.
864      * @since 1.4
865      */

866     public AttributedCharacterIterator JavaDoc formatToCharacterIterator(Object JavaDoc arguments) {
867         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
868         ArrayList JavaDoc iterators = new ArrayList JavaDoc();
869
870         if (arguments == null) {
871             throw new NullPointerException JavaDoc(
872                    "formatToCharacterIterator must be passed non-null object");
873         }
874         subformat((Object JavaDoc[]) arguments, result, null, iterators);
875         if (iterators.size() == 0) {
876             return createAttributedCharacterIterator("");
877         }
878         return createAttributedCharacterIterator(
879                      (AttributedCharacterIterator JavaDoc[])iterators.toArray(
880                      new AttributedCharacterIterator JavaDoc[iterators.size()]));
881     }
882
883     /**
884      * Parses the string.
885      *
886      * <p>Caveats: The parse may fail in a number of circumstances.
887      * For example:
888      * <ul>
889      * <li>If one of the arguments does not occur in the pattern.
890      * <li>If the format of an argument loses information, such as
891      * with a choice format where a large number formats to "many".
892      * <li>Does not yet handle recursion (where
893      * the substituted strings contain {