]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/eclipse/plugins/com.ibm.icu.base/src/com/ibm/icu/text/MessageFormat.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / eclipse / plugins / com.ibm.icu.base / src / com / ibm / icu / text / MessageFormat.java
1 /*\r
2  *******************************************************************************\r
3  * Copyright (C) 2004-2008, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.text;\r
8 \r
9 import java.lang.reflect.InvocationTargetException;\r
10 import java.lang.reflect.Method;\r
11 import java.text.FieldPosition;\r
12 import java.text.Format;\r
13 import java.text.ParseException;\r
14 import java.text.ParsePosition;\r
15 import java.util.Locale;\r
16 \r
17 import com.ibm.icu.util.ULocale;\r
18 \r
19 /**\r
20  * <code>MessageFormat</code> provides a means to produce concatenated\r
21  * messages in language-neutral way. Use this to construct messages\r
22  * displayed for end users.\r
23  *\r
24  * <p>\r
25  * <code>MessageFormat</code> takes a set of objects, formats them, then\r
26  * inserts the formatted strings into the pattern at the appropriate places.\r
27  *\r
28  * <p>\r
29  * <strong>Note:</strong>\r
30  * <code>MessageFormat</code> differs from the other <code>Format</code>\r
31  * classes in that you create a <code>MessageFormat</code> object with one\r
32  * of its constructors (not with a <code>getInstance</code> style factory\r
33  * method). The factory methods aren't necessary because <code>MessageFormat</code>\r
34  * itself doesn't implement locale specific behavior. Any locale specific\r
35  * behavior is defined by the pattern that you provide as well as the\r
36  * subformats used for inserted arguments.\r
37  *\r
38  * <h4><a name="patterns">Patterns and Their Interpretation</a></h4>\r
39  *\r
40  * <code>MessageFormat</code> uses patterns of the following form:\r
41  * <blockquote><pre>\r
42  * <i>MessageFormatPattern:</i>\r
43  *         <i>String</i>\r
44  *         <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i>\r
45  *\r
46  * <i>FormatElement:</i>\r
47  *         { <i>ArgumentIndex</i> }\r
48  *         { <i>ArgumentIndex</i> , <i>FormatType</i> }\r
49  *         { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> }\r
50  *\r
51  * <i>FormatType: one of </i>\r
52  *         number date time choice\r
53  *\r
54  * <i>FormatStyle:</i>\r
55  *         short\r
56  *         medium\r
57  *         long\r
58  *         full\r
59  *         integer\r
60  *         currency\r
61  *         percent\r
62  *         <i>SubformatPattern</i>\r
63  *\r
64  * <i>String:</i>\r
65  *         <i>StringPart<sub>opt</sub></i>\r
66  *         <i>String</i> <i>StringPart</i>\r
67  *\r
68  * <i>StringPart:</i>\r
69  *         ''\r
70  *         ' <i>QuotedString</i> '\r
71  *         <i>UnquotedString</i>\r
72  *\r
73  * <i>SubformatPattern:</i>\r
74  *         <i>SubformatPatternPart<sub>opt</sub></i>\r
75  *         <i>SubformatPattern</i> <i>SubformatPatternPart</i>\r
76  *\r
77  * <i>SubFormatPatternPart:</i>\r
78  *         ' <i>QuotedPattern</i> '\r
79  *         <i>UnquotedPattern</i>\r
80  * </pre></blockquote>\r
81  *\r
82  * <p>\r
83  * Within a <i>String</i>, <code>"''"</code> represents a single\r
84  * quote. A <i>QuotedString</i> can contain arbitrary characters\r
85  * except single quotes; the surrounding single quotes are removed.\r
86  * An <i>UnquotedString</i> can contain arbitrary characters\r
87  * except single quotes and left curly brackets. Thus, a string that\r
88  * should result in the formatted message "'{0}'" can be written as\r
89  * <code>"'''{'0}''"</code> or <code>"'''{0}'''"</code>.\r
90  * <p>\r
91  * Within a <i>SubformatPattern</i>, different rules apply.\r
92  * A <i>QuotedPattern</i> can contain arbitrary characters\r
93  * except single quotes; but the surrounding single quotes are\r
94  * <strong>not</strong> removed, so they may be interpreted by the\r
95  * subformat. For example, <code>"{1,number,$'#',##}"</code> will\r
96  * produce a number format with the pound-sign quoted, with a result\r
97  * such as: "$#31,45".\r
98  * An <i>UnquotedPattern</i> can contain arbitrary characters\r
99  * except single quotes, but curly braces within it must be balanced.\r
100  * For example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code>\r
101  * are valid subformat patterns, but <code>"ab {0'}' de"</code> and\r
102  * <code>"ab } de"</code> are not.\r
103  * <p>\r
104  * <dl><dt><b>Warning:</b><dd>The rules for using quotes within message\r
105  * format patterns unfortunately have shown to be somewhat confusing.\r
106  * In particular, it isn't always obvious to localizers whether single\r
107  * quotes need to be doubled or not. Make sure to inform localizers about\r
108  * the rules, and tell them (for example, by using comments in resource\r
109  * bundle source files) which strings will be processed by MessageFormat.\r
110  * Note that localizers may need to use single quotes in translated\r
111  * strings where the original version doesn't have them.\r
112  * <br>Note also that the simplest way to avoid the problem is to\r
113  * use the real apostrophe (single quote) character \u2019 (') for\r
114  * human-readable text, and to use the ASCII apostrophe (\u0027 ' )\r
115  * only in program syntax, like quoting in MessageFormat.\r
116  * See the annotations for U+0027 Apostrophe in The Unicode Standard.</p>\r
117  * </dl>\r
118  * <p>\r
119  * The <i>ArgumentIndex</i> value is a non-negative integer written\r
120  * using the digits '0' through '9', and represents an index into the\r
121  * <code>arguments</code> array passed to the <code>format</code> methods\r
122  * or the result array returned by the <code>parse</code> methods.\r
123  * <p>\r
124  * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create\r
125  * a <code>Format</code> instance for the format element. The following\r
126  * table shows how the values map to Format instances. Combinations not\r
127  * shown in the table are illegal. A <i>SubformatPattern</i> must\r
128  * be a valid pattern string for the Format subclass used.\r
129  * <p>\r
130  * <table border=1>\r
131  *    <tr>\r
132  *       <th>Format Type\r
133  *       <th>Format Style\r
134  *       <th>Subformat Created\r
135  *    <tr>\r
136  *       <td colspan=2><i>(none)</i>\r
137  *       <td><code>null</code>\r
138  *    <tr>\r
139  *       <td rowspan=5><code>number</code>\r
140  *       <td><i>(none)</i>\r
141  *       <td><code>NumberFormat.getInstance(getLocale())</code>\r
142  *    <tr>\r
143  *       <td><code>integer</code>\r
144  *       <td><code>NumberFormat.getIntegerInstance(getLocale())</code>\r
145  *    <tr>\r
146  *       <td><code>currency</code>\r
147  *       <td><code>NumberFormat.getCurrencyInstance(getLocale())</code>\r
148  *    <tr>\r
149  *       <td><code>percent</code>\r
150  *       <td><code>NumberFormat.getPercentInstance(getLocale())</code>\r
151  *    <tr>\r
152  *       <td><i>SubformatPattern</i>\r
153  *       <td><code>new DecimalFormat(subformatPattern, new DecimalFormatSymbols(getLocale()))</code>\r
154  *    <tr>\r
155  *       <td rowspan=6><code>date</code>\r
156  *       <td><i>(none)</i>\r
157  *       <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>\r
158  *    <tr>\r
159  *       <td><code>short</code>\r
160  *       <td><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code>\r
161  *    <tr>\r
162  *       <td><code>medium</code>\r
163  *       <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>\r
164  *    <tr>\r
165  *       <td><code>long</code>\r
166  *       <td><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code>\r
167  *    <tr>\r
168  *       <td><code>full</code>\r
169  *       <td><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code>\r
170  *    <tr>\r
171  *       <td><i>SubformatPattern</i>\r
172  *       <td><code>new SimpleDateFormat(subformatPattern, getLocale())\r
173  *    <tr>\r
174  *       <td rowspan=6><code>time</code>\r
175  *       <td><i>(none)</i>\r
176  *       <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>\r
177  *    <tr>\r
178  *       <td><code>short</code>\r
179  *       <td><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code>\r
180  *    <tr>\r
181  *       <td><code>medium</code>\r
182  *       <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>\r
183  *    <tr>\r
184  *       <td><code>long</code>\r
185  *       <td><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code>\r
186  *    <tr>\r
187  *       <td><code>full</code>\r
188  *       <td><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code>\r
189  *    <tr>\r
190  *       <td><i>SubformatPattern</i>\r
191  *       <td><code>new SimpleDateFormat(subformatPattern, getLocale())\r
192  *    <tr>\r
193  *       <td><code>choice</code>\r
194  *       <td><i>SubformatPattern</i>\r
195  *       <td><code>new ChoiceFormat(subformatPattern)</code>\r
196  * </table>\r
197  * <p>\r
198  *\r
199  * <h4>Usage Information</h4>\r
200  *\r
201  * <p>\r
202  * Here are some examples of usage:\r
203  * <blockquote>\r
204  * <pre>\r
205  * Object[] arguments = {\r
206  *     new Integer(7),\r
207  *     new Date(System.currentTimeMillis()),\r
208  *     "a disturbance in the Force"\r
209  * };\r
210  *\r
211  * String result = MessageFormat.format(\r
212  *     "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",\r
213  *     arguments);\r
214  *\r
215  * <em>output</em>: At 12:30 PM on Jul 3, 2053, there was a disturbance\r
216  *           in the Force on planet 7.\r
217  *\r
218  * </pre>\r
219  * </blockquote>\r
220  * Typically, the message format will come from resources, and the\r
221  * arguments will be dynamically set at runtime.\r
222  *\r
223  * <p>\r
224  * Example 2:\r
225  * <blockquote>\r
226  * <pre>\r
227  * Object[] testArgs = {new Long(3), "MyDisk"};\r
228  *\r
229  * MessageFormat form = new MessageFormat(\r
230  *     "The disk \"{1}\" contains {0} file(s).");\r
231  *\r
232  * System.out.println(form.format(testArgs));\r
233  *\r
234  * // output, with different testArgs\r
235  * <em>output</em>: The disk "MyDisk" contains 0 file(s).\r
236  * <em>output</em>: The disk "MyDisk" contains 1 file(s).\r
237  * <em>output</em>: The disk "MyDisk" contains 1,273 file(s).\r
238  * </pre>\r
239  * </blockquote>\r
240  *\r
241  * <p>\r
242  * For more sophisticated patterns, you can use a <code>ChoiceFormat</code> to get\r
243  * output such as:\r
244  * <blockquote>\r
245  * <pre>\r
246  * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");\r
247  * double[] filelimits = {0,1,2};\r
248  * String[] filepart = {"no files","one file","{0,number} files"};\r
249  * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);\r
250  * form.setFormatByArgumentIndex(0, fileform);\r
251  *\r
252  * Object[] testArgs = {new Long(12373), "MyDisk"};\r
253  *\r
254  * System.out.println(form.format(testArgs));\r
255  *\r
256  * // output, with different testArgs\r
257  * output: The disk "MyDisk" contains no files.\r
258  * output: The disk "MyDisk" contains one file.\r
259  * output: The disk "MyDisk" contains 1,273 files.\r
260  * </pre>\r
261  * </blockquote>\r
262  * You can either do this programmatically, as in the above example,\r
263  * or by using a pattern (see\r
264  * {@link ChoiceFormat}\r
265  * for more information) as in:\r
266  * <blockquote>\r
267  * <pre>\r
268  * form.applyPattern(\r
269  *    "There {0,choice,0#are no files|1#is one file|1&lt;are {0,number,integer} files}.");\r
270  * </pre>\r
271  * </blockquote>\r
272  * <p>\r
273  * <strong>Note:</strong> As we see above, the string produced\r
274  * by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated specially;\r
275  * occurances of '{' are used to indicated subformats, and cause recursion.\r
276  * If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code>\r
277  * programmatically (instead of using the string patterns), then be careful not to\r
278  * produce a format that recurses on itself, which will cause an infinite loop.\r
279  * <p>\r
280  * When a single argument is parsed more than once in the string, the last match\r
281  * will be the final result of the parsing.  For example,\r
282  * <pre>\r
283  * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");\r
284  * Object[] objs = {new Double(3.1415)};\r
285  * String result = mf.format( objs );\r
286  * // result now equals "3.14, 3.1"\r
287  * objs = null;\r
288  * objs = mf.parse(result, new ParsePosition(0));\r
289  * // objs now equals {new Double(3.1)}\r
290  * </pre>\r
291  * <p>\r
292  * Likewise, parsing with a MessageFormat object using patterns containing\r
293  * multiple occurances of the same argument would return the last match.  For\r
294  * example,\r
295  * <pre>\r
296  * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");\r
297  * String forParsing = "x, y, z";\r
298  * Object[] objs = mf.parse(forParsing, new ParsePosition(0));\r
299  * // result now equals {new String("z")}\r
300  * </pre>\r
301  *\r
302  * <h4><a name="synchronization">Synchronization</a></h4>\r
303  *\r
304  * <p>\r
305  * Message formats are not synchronized.\r
306  * It is recommended to create separate format instances for each thread.\r
307  * If multiple threads access a format concurrently, it must be synchronized\r
308  * externally.\r
309  *\r
310  * @see          java.util.Locale\r
311  * @see          Format\r
312  * @see          NumberFormat\r
313  * @see          DecimalFormat\r
314  * @see          ChoiceFormat\r
315  * @author       Mark Davis\r
316  * @stable ICU 3.0\r
317  */\r
318 public class MessageFormat extends Format {\r
319     static final long serialVersionUID = 1L;\r
320 \r
321     /**\r
322      * @internal\r
323      */\r
324     public final java.text.MessageFormat messageFormat;\r
325         \r
326     /**\r
327      * @internal\r
328      * @param delegate the DateFormat to which to delegate\r
329      */\r
330     public MessageFormat(java.text.MessageFormat delegate) {\r
331         this.messageFormat = delegate;\r
332     }\r
333 \r
334     /**\r
335      * Constructs a MessageFormat for the default locale and the\r
336      * specified pattern.\r
337      * The constructor first sets the locale, then parses the pattern and\r
338      * creates a list of subformats for the format elements contained in it.\r
339      * Patterns and their interpretation are specified in the\r
340      * <a href="#patterns">class description</a>.\r
341      *\r
342      * @param pattern the pattern for this message format\r
343      * @exception IllegalArgumentException if the pattern is invalid\r
344      * @stable ICU 3.0\r
345      */\r
346     public MessageFormat(String pattern) {\r
347         this(new java.text.MessageFormat(pattern));\r
348     }\r
349 \r
350     /**\r
351      * Constructs a MessageFormat for the specified locale and\r
352      * pattern.\r
353      * The constructor first sets the locale, then parses the pattern and\r
354      * creates a list of subformats for the format elements contained in it.\r
355      * Patterns and their interpretation are specified in the\r
356      * <a href="#patterns">class description</a>.\r
357      *\r
358      * @param pattern the pattern for this message format\r
359      * @param locale the locale for this message format\r
360      * @exception IllegalArgumentException if the pattern is invalid\r
361      * @stable ICU 3.0\r
362      */\r
363     public MessageFormat(String pattern, Locale locale) {\r
364         // locale is ignored\r
365         this(new java.text.MessageFormat(pattern));\r
366     }\r
367 \r
368     /**\r
369      * Constructs a MessageFormat for the specified locale and\r
370      * pattern.\r
371      * The constructor first sets the locale, then parses the pattern and\r
372      * creates a list of subformats for the format elements contained in it.\r
373      * Patterns and their interpretation are specified in the\r
374      * <a href="#patterns">class description</a>.\r
375      *\r
376      * @param pattern the pattern for this message format\r
377      * @param locale the locale for this message format\r
378      * @exception IllegalArgumentException if the pattern is invalid\r
379      * @stable ICU 3.2\r
380      */\r
381     public MessageFormat(String pattern, ULocale locale) {\r
382         // locale is ignored\r
383         this(pattern);\r
384     }\r
385 \r
386     /**\r
387      * Sets the locale to be used when creating or comparing subformats.\r
388      * This affects subsequent calls to the {@link #applyPattern applyPattern}\r
389      * and {@link #toPattern toPattern} methods as well as to the\r
390      * <code>format</code> and\r
391      * {@link #formatToCharacterIterator formatToCharacterIterator} methods.\r
392      *\r
393      * @param locale the locale to be used when creating or comparing subformats\r
394      * @stable ICU 3.0\r
395      */\r
396     public void setLocale(Locale locale) {\r
397         messageFormat.setLocale(locale);\r
398     }\r
399 \r
400     /**\r
401      * Sets the locale to be used when creating or comparing subformats.\r
402      * This affects subsequent calls to the {@link #applyPattern applyPattern}\r
403      * and {@link #toPattern toPattern} methods as well as to the\r
404      * <code>format</code> and\r
405      * {@link #formatToCharacterIterator formatToCharacterIterator} methods.\r
406      *\r
407      * @param locale the locale to be used when creating or comparing subformats\r
408      * @stable ICU 3.2\r
409      */\r
410     public void setLocale(ULocale locale) {\r
411         messageFormat.setLocale(locale.toLocale());\r
412     }\r
413 \r
414     /**\r
415      * Gets the locale that's used when creating or comparing subformats.\r
416      *\r
417      * @return the locale used when creating or comparing subformats\r
418      * @stable ICU 3.0\r
419      */\r
420     public Locale getLocale() {\r
421         return messageFormat.getLocale();\r
422     }\r
423 \r
424     /**\r
425      * Gets the locale that's used when creating or comparing subformats.\r
426      *\r
427      * @return the locale used when creating or comparing subformats\r
428      * @stable ICU 3.2\r
429      */\r
430     public ULocale getULocale() {\r
431         return ULocale.forLocale(messageFormat.getLocale());\r
432     }\r
433 \r
434     /**\r
435      * Sets the pattern used by this message format.\r
436      * The method parses the pattern and creates a list of subformats\r
437      * for the format elements contained in it.\r
438      * Patterns and their interpretation are specified in the\r
439      * <a href="#patterns">class description</a>.\r
440      * \r
441      * @param pattern the pattern for this message format\r
442      * @exception IllegalArgumentException if the pattern is invalid\r
443      * @stable ICU 3.0\r
444      */\r
445    public void applyPattern(String pattern) {\r
446        messageFormat.applyPattern(pattern);\r
447    }\r
448 \r
449    /**\r
450     * Returns a pattern representing the current state of the message format.\r
451     * The string is constructed from internal information and therefore\r
452     * does not necessarily equal the previously applied pattern. \r
453     *\r
454     * @return a pattern representing the current state of the message format\r
455     * @stable ICU 3.0\r
456     */\r
457    public String toPattern() {\r
458        return messageFormat.toPattern();\r
459    }\r
460  \r
461    /**\r
462     * Sets the formats to use for the values passed into\r
463     * <code>format</code> methods or returned from <code>parse</code>\r
464     * methods. The indices of elements in <code>newFormats</code>\r
465     * correspond to the argument indices used in the previously set\r
466     * pattern string.\r
467     * The order of formats in <code>newFormats</code> thus corresponds to\r
468     * the order of elements in the <code>arguments</code> array passed\r
469     * to the <code>format</code> methods or the result array returned\r
470     * by the <code>parse</code> methods.\r
471     * <p>\r
472     * If an argument index is used for more than one format element\r
473     * in the pattern string, then the corresponding new format is used\r
474     * for all such format elements. If an argument index is not used\r
475     * for any format element in the pattern string, then the\r
476     * corresponding new format is ignored. If fewer formats are provided\r
477     * than needed, then only the formats for argument indices less\r
478     * than <code>newFormats.length</code> are replaced.\r
479     *\r
480     * @param newFormats the new formats to use\r
481     * @exception NullPointerException if <code>newFormats</code> is null\r
482     * @stable ICU 3.0\r
483     * @throws UnsupportedOperationException if the underlying JVM does not\r
484     * support this method.\r
485     */\r
486    public void setFormatsByArgumentIndex(Format[] newFormats) {\r
487        if (sfsbai == null) {\r
488            synchronized (missing) {\r
489                try {\r
490                    Class[] params = { Format[].class };\r
491                    sfsbai = java.text.MessageFormat.class.getMethod("setFormatsByArgumentIndex", params);\r
492                }\r
493                catch (NoSuchMethodException e) {\r
494                    sfsbai = missing;\r
495                }\r
496            }\r
497        }\r
498        if (sfsbai != missing) {\r
499            try {\r
500                Format[] unwrapped = new Format[newFormats.length];\r
501                for (int i = 0; i < newFormats.length; ++i) {\r
502                    unwrapped[i] = unwrap(newFormats[i]);\r
503                }\r
504                Object[] args = { unwrapped };\r
505                ((Method)sfsbai).invoke(messageFormat, args);\r
506                return;\r
507            }\r
508            catch (IllegalAccessException e) {\r
509                // can't happen\r
510            }\r
511            catch (InvocationTargetException e) {\r
512                // can't happen\r
513            }\r
514        }\r
515        throw new UnsupportedOperationException();\r
516     }\r
517    private static Object sfsbai;\r
518    \r
519    /**\r
520     * Sets the formats to use for the format elements in the\r
521     * previously set pattern string.\r
522     * The order of formats in <code>newFormats</code> corresponds to\r
523     * the order of format elements in the pattern string.\r
524     * <p>\r
525     * If more formats are provided than needed by the pattern string,\r
526     * the remaining ones are ignored. If fewer formats are provided\r
527     * than needed, then only the first <code>newFormats.length</code>\r
528     * formats are replaced.\r
529     * <p>\r
530     * Since the order of format elements in a pattern string often\r
531     * changes during localization, it is generally better to use the\r
532     * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}\r
533     * method, which assumes an order of formats corresponding to the\r
534     * order of elements in the <code>arguments</code> array passed to\r
535     * the <code>format</code> methods or the result array returned by\r
536     * the <code>parse</code> methods.\r
537     *\r
538     * @param newFormats the new formats to use\r
539     * @exception NullPointerException if <code>newFormats</code> is null\r
540     * @stable ICU 3.0\r
541     */\r
542    public void setFormats(Format[] newFormats) {\r
543        messageFormat.setFormats(newFormats);\r
544    }\r
545 \r
546    /**\r
547     * Sets the format to use for the format elements within the\r
548     * previously set pattern string that use the given argument\r
549     * index.\r
550     * The argument index is part of the format element definition and\r
551     * represents an index into the <code>arguments</code> array passed\r
552     * to the <code>format</code> methods or the result array returned\r
553     * by the <code>parse</code> methods.\r
554     * <p>\r
555     * If the argument index is used for more than one format element\r
556     * in the pattern string, then the new format is used for all such\r
557     * format elements. If the argument index is not used for any format\r
558     * element in the pattern string, then the new format is ignored.\r
559     *\r
560     * @param argumentIndex the argument index for which to use the new format\r
561     * @param newFormat the new format to use\r
562     * @stable ICU 3.0\r
563     * @throws UnsupportedOperationException if the underlying JVM does not\r
564     * support this method.\r
565     */\r
566    public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) {\r
567        if (sfbai == null) {\r
568            synchronized (missing) {\r
569                try {\r
570                    Class[] params = { Integer.TYPE, Format.class };\r
571                    sfbai = java.text.MessageFormat.class.getMethod("setFormatByArgumentIndex", params);\r
572                }\r
573                catch (NoSuchMethodException e) {\r
574                    sfbai = missing;\r
575                }\r
576            }\r
577        }\r
578        if (sfbai != missing) {\r
579            try {\r
580                Object[] args = { new Integer(argumentIndex), newFormat };\r
581                ((Method)sfbai).invoke(messageFormat, args);\r
582                return;\r
583            }\r
584            catch (IllegalAccessException e) {\r
585                // can't happen\r
586            }\r
587            catch (InvocationTargetException e) {\r
588                // can't happen\r
589            }\r
590        }\r
591        throw new UnsupportedOperationException();\r
592    }\r
593    private static Object sfbai;\r
594 \r
595    /**\r
596     * Sets the format to use for the format element with the given\r
597     * format element index within the previously set pattern string.\r
598     * The format element index is the zero-based number of the format\r
599     * element counting from the start of the pattern string.\r
600     * <p>\r
601     * Since the order of format elements in a pattern string often\r
602     * changes during localization, it is generally better to use the\r
603     * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}\r
604     * method, which accesses format elements based on the argument\r
605     * index they specify.\r
606     *\r
607     * @param formatElementIndex the index of a format element within the pattern\r
608     * @param newFormat the format to use for the specified format element\r
609     * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or\r
610     *            larger than the number of format elements in the pattern string\r
611     * @stable ICU 3.0\r
612     */\r
613    public void setFormat(int formatElementIndex, Format newFormat) {\r
614        messageFormat.setFormat(formatElementIndex, unwrap(newFormat));\r
615    }\r
616 \r
617    /**\r
618     * Gets the formats used for the values passed into\r
619     * <code>format</code> methods or returned from <code>parse</code>\r
620     * methods. The indices of elements in the returned array\r
621     * correspond to the argument indices used in the previously set\r
622     * pattern string.\r
623     * The order of formats in the returned array thus corresponds to\r
624     * the order of elements in the <code>arguments</code> array passed\r
625     * to the <code>format</code> methods or the result array returned\r
626     * by the <code>parse</code> methods.\r
627     * <p>\r
628     * If an argument index is used for more than one format element\r
629     * in the pattern string, then the format used for the last such\r
630     * format element is returned in the array. If an argument index\r
631     * is not used for any format element in the pattern string, then\r
632     * null is returned in the array.\r
633     *\r
634     * @return the formats used for the arguments within the pattern\r
635     * @stable ICU 3.0\r
636     * @throws UnsupportedOperationException if the underlying JVM does not \r
637     * support this method.\r
638     */\r
639    public Format[] getFormatsByArgumentIndex() {\r
640        if (gfbai == null) {\r
641            synchronized (missing) {\r
642                try {\r
643                    gfbai = java.text.MessageFormat.class.getMethod("getFormatsByArgumentIndex", null);\r
644                }\r
645                catch (NoSuchMethodException e) {\r
646                    gfbai = missing;\r
647                }\r
648            }\r
649        }\r
650        if (gfbai != missing) {\r
651            try {\r
652                Format[] result = (Format[])((Method)gfbai).invoke(messageFormat, null);\r
653                for (int i = 0; i < result.length; ++i) {\r
654                    result[i] = wrap(result[i]);\r
655                }\r
656                return result;\r
657            }\r
658            catch (IllegalAccessException e) {\r
659                // can't happen\r
660            }\r
661            catch (InvocationTargetException e) {\r
662                // can't happen\r
663            }\r
664        }\r
665        throw new UnsupportedOperationException();\r
666    }\r
667    private static Object gfbai;\r
668    private static final Object missing = new Object();\r
669 \r
670    /**\r
671     * Gets the formats used for the format elements in the\r
672     * previously set pattern string.\r
673     * The order of formats in the returned array corresponds to\r
674     * the order of format elements in the pattern string.\r
675     * <p>\r
676     * Since the order of format elements in a pattern string often\r
677     * changes during localization, it's generally better to use the\r
678     * {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex}\r
679     * method, which assumes an order of formats corresponding to the\r
680     * order of elements in the <code>arguments</code> array passed to\r
681     * the <code>format</code> methods or the result array returned by\r
682     * the <code>parse</code> methods.\r
683     *\r
684     * @return the formats used for the format elements in the pattern\r
685     * @stable ICU 3.0\r
686     */\r
687    public Format[] getFormats() {\r
688        Format[] result = messageFormat.getFormats();\r
689        for (int i = 0; i < result.length; ++i) {\r
690            result[i] = wrap(result[i]);\r
691        }\r
692        return result;\r
693    }\r
694 \r
695    /**\r
696     * Formats an array of objects and appends the <code>MessageFormat</code>'s\r
697     * pattern, with format elements replaced by the formatted objects, to the\r
698     * provided <code>StringBuffer</code>.\r
699     * <p>\r
700     * The text substituted for the individual format elements is derived from\r
701     * the current subformat of the format element and the\r
702     * <code>arguments</code> element at the format element's argument index\r
703     * as indicated by the first matching line of the following table. An\r
704     * argument is <i>unavailable</i> if <code>arguments</code> is\r
705     * <code>null</code> or has fewer than argumentIndex+1 elements.\r
706     * <p>\r
707     * <table border=1>\r
708     *    <tr>\r
709     *       <th>Subformat\r
710     *       <th>Argument\r
711     *       <th>Formatted Text\r
712     *    <tr>\r
713     *       <td><i>any</i>\r
714     *       <td><i>unavailable</i>\r
715     *       <td><code>"{" + argumentIndex + "}"</code>\r
716     *    <tr>\r
717     *       <td><i>any</i>\r
718     *       <td><code>null</code>\r
719     *       <td><code>"null"</code>\r
720     *    <tr>\r
721     *       <td><code>instanceof ChoiceFormat</code>\r
722     *       <td><i>any</i>\r
723     *       <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>\r
724     *           (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :\r
725     *           subformat.format(argument)</code>\r
726     *    <tr>\r
727     *       <td><code>!= null</code>\r
728     *       <td><i>any</i>\r
729     *       <td><code>subformat.format(argument)</code>\r
730     *    <tr>\r
731     *       <td><code>null</code>\r
732     *       <td><code>instanceof Number</code>\r
733     *       <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>\r
734     *    <tr>\r
735     *       <td><code>null</code>\r
736     *       <td><code>instanceof Date</code>\r
737     *       <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>\r
738     *    <tr>\r
739     *       <td><code>null</code>\r
740     *       <td><code>instanceof String</code>\r
741     *       <td><code>argument</code>\r
742     *    <tr>\r
743     *       <td><code>null</code>\r
744     *       <td><i>any</i>\r
745     *       <td><code>argument.toString()</code>\r
746     * </table>\r
747     * <p>\r
748     * If <code>pos</code> is non-null, and refers to\r
749     * <code>Field.ARGUMENT</code>, the location of the first formatted\r
750     * string will be returned.\r
751     *\r
752     * @param arguments an array of objects to be formatted and substituted.\r
753     * @param result where text is appended.\r
754     * @param pos On input: an alignment field, if desired.\r
755     *            On output: the offsets of the alignment field.\r
756     * @exception IllegalArgumentException if an argument in the\r
757     *            <code>arguments</code> array is not of the type\r
758     *            expected by the format element(s) that use it.\r
759     * @stable ICU 3.0\r
760     */\r
761    public final StringBuffer format(Object[] arguments, StringBuffer result,\r
762        FieldPosition pos)\r
763    {\r
764        return messageFormat.format(arguments, result, pos);\r
765    }\r
766 \r
767    /**\r
768     * Creates a MessageFormat with the given pattern and uses it\r
769     * to format the given arguments. This is equivalent to\r
770     * <blockquote>\r
771     *     <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>\r
772     * </blockquote>\r
773     *\r
774     * @exception IllegalArgumentException if the pattern is invalid,\r
775     *            or if an argument in the <code>arguments</code> array\r
776     *            is not of the type expected by the format element(s)\r
777     *            that use it.\r
778     * @stable ICU 3.0\r
779     */\r
780    public static String format(String pattern, Object[] arguments) {\r
781        return java.text.MessageFormat.format(pattern, arguments);\r
782    }\r
783 \r
784    // Overrides\r
785    /**\r
786     * Formats an array of objects and appends the <code>MessageFormat</code>'s\r
787     * pattern, with format elements replaced by the formatted objects, to the\r
788     * provided <code>StringBuffer</code>.\r
789     * This is equivalent to\r
790     * <blockquote>\r
791     *     <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>\r
792     * </blockquote>\r
793     *\r
794     * @param arguments an array of objects to be formatted and substituted.\r
795     * @param result where text is appended.\r
796     * @param pos On input: an alignment field, if desired.\r
797     *            On output: the offsets of the alignment field.\r
798     * @exception IllegalArgumentException if an argument in the\r
799     *            <code>arguments</code> array is not of the type\r
800     *            expected by the format element(s) that use it.\r
801     * @stable ICU 3.0\r
802     */\r
803    public final StringBuffer format(Object arguments, StringBuffer result,\r
804        FieldPosition pos)\r
805    {\r
806        return messageFormat.format(arguments, result, pos);\r
807    }\r
808 \r
809 \r
810    /**\r
811     * Parses the string.\r
812     *\r
813     * <p>Caveats: The parse may fail in a number of circumstances.\r
814     * For example:\r
815     * <ul>\r
816     * <li>If one of the arguments does not occur in the pattern.\r
817     * <li>If the format of an argument loses information, such as\r
818     *     with a choice format where a large number formats to "many".\r
819     * <li>Does not yet handle recursion (where\r
820     *     the substituted strings contain {n} references.)\r
821     * <li>Will not always find a match (or the correct match)\r
822     *     if some part of the parse is ambiguous.\r
823     *     For example, if the pattern "{1},{2}" is used with the\r
824     *     string arguments {"a,b", "c"}, it will format as "a,b,c".\r
825     *     When the result is parsed, it will return {"a", "b,c"}.\r
826     * <li>If a single argument is parsed more than once in the string,\r
827     *     then the later parse wins.\r
828     * </ul>\r
829     * When the parse fails, use ParsePosition.getErrorIndex() to find out\r
830     * where in the string did the parsing failed.  The returned error\r
831     * index is the starting offset of the sub-patterns that the string\r
832     * is comparing with.  For example, if the parsing string "AAA {0} BBB"\r
833     * is comparing against the pattern "AAD {0} BBB", the error index is\r
834     * 0. When an error occurs, the call to this method will return null.\r
835     * If the source is null, return an empty array.\r
836     * @stable ICU 3.0\r
837     */\r
838    public Object[] parse(String source, ParsePosition pos) {\r
839        return messageFormat.parse(source, pos);\r
840    }\r
841 \r
842    /**\r
843     * Parses text from the beginning of the given string to produce an object\r
844     * array.\r
845     * The method may not use the entire text of the given string.\r
846     * <p>\r
847     * See the {@link #parse(String, ParsePosition)} method for more information\r
848     * on message parsing.\r
849     *\r
850     * @param source A <code>String</code> whose beginning should be parsed.\r
851     * @return An <code>Object</code> array parsed from the string.\r
852     * @exception ParseException if the beginning of the specified string\r
853     *            cannot be parsed.\r
854     * @stable ICU 3.0\r
855     */\r
856    public Object[] parse(String source) throws ParseException {\r
857        return messageFormat.parse(source);\r
858    }\r
859 \r
860    /**\r
861     * Parses text from a string to produce an object array.\r
862     * <p>\r
863     * The method attempts to parse text starting at the index given by\r
864     * <code>pos</code>.\r
865     * If parsing succeeds, then the index of <code>pos</code> is updated\r
866     * to the index after the last character used (parsing does not necessarily\r
867     * use all characters up to the end of the string), and the parsed\r
868     * object array is returned. The updated <code>pos</code> can be used to\r
869     * indicate the starting point for the next call to this method.\r
870     * If an error occurs, then the index of <code>pos</code> is not\r
871     * changed, the error index of <code>pos</code> is set to the index of\r
872     * the character where the error occurred, and null is returned.\r
873     * <p>\r
874     * See the {@link #parse(String, ParsePosition)} method for more information\r
875     * on message parsing.\r
876     *\r
877     * @param source A <code>String</code>, part of which should be parsed.\r
878     * @param pos A <code>ParsePosition</code> object with index and error\r
879     *            index information as described above.\r
880     * @return An <code>Object</code> array parsed from the string. In case of\r
881     *         error, returns null.\r
882     * @exception NullPointerException if <code>pos</code> is null.\r
883     * @stable ICU 3.0\r
884     */\r
885    public Object parseObject(String source, ParsePosition pos) {\r
886        return messageFormat.parse(source, pos);\r
887    }\r
888 \r
889    /**\r
890     * Convert an 'apostrophe-friendly' pattern into a standard\r
891     * pattern.  Standard patterns treat all apostrophes as\r
892     * quotes, which is problematic in some languages, e.g. \r
893     * French, where apostrophe is commonly used.  This utility\r
894     * assumes that only an unpaired apostrophe immediately before\r
895     * a brace is a true quote.  Other unpaired apostrophes are paired,\r
896     * and the resulting standard pattern string is returned.\r
897     *\r
898     * <p><b>Note</b> it is not guaranteed that the returned pattern\r
899     * is indeed a valid pattern.  The only effect is to convert\r
900     * between patterns having different quoting semantics.\r
901     *\r
902     * @param pattern the 'apostrophe-friendly' patttern to convert\r
903     * @return the standard equivalent of the original pattern\r
904     * @stable ICU 3.4\r
905     */\r
906    public static String autoQuoteApostrophe(String pattern) {\r
907        StringBuffer buf = new StringBuffer(pattern.length()*2);\r
908        int state = STATE_INITIAL;\r
909        int braceCount = 0;\r
910        for (int i = 0, j = pattern.length(); i < j; ++i) {\r
911            char c = pattern.charAt(i);\r
912            switch (state) {\r
913            case STATE_INITIAL:\r
914                switch (c) {\r
915                case SINGLE_QUOTE:\r
916                    state = STATE_SINGLE_QUOTE;\r
917                    break;\r
918                case CURLY_BRACE_LEFT:\r
919                    state = STATE_MSG_ELEMENT;\r
920                    ++braceCount;\r
921                    break;\r
922                }\r
923                break;\r
924            case STATE_SINGLE_QUOTE:\r
925                switch (c) {\r
926                case SINGLE_QUOTE:\r
927                    state = STATE_INITIAL;\r
928                    break;\r
929                case CURLY_BRACE_LEFT:\r
930                case CURLY_BRACE_RIGHT:\r
931                    state = STATE_IN_QUOTE;\r
932                    break;\r
933                default:\r
934                    buf.append(SINGLE_QUOTE);\r
935                state = STATE_INITIAL;\r
936                break;\r
937                }\r
938                break;\r
939            case STATE_IN_QUOTE:\r
940                switch (c) {\r
941                case SINGLE_QUOTE:\r
942                    state = STATE_INITIAL;\r
943                    break;\r
944                }\r
945                break;\r
946            case STATE_MSG_ELEMENT:\r
947                switch (c) {\r
948                case CURLY_BRACE_LEFT:\r
949                    ++braceCount;\r
950                    break;\r
951                case CURLY_BRACE_RIGHT:\r
952                    if (--braceCount == 0) {\r
953                        state = STATE_INITIAL;\r
954                    }\r
955                    break;\r
956                }\r
957                break;\r
958            default: // Never happens.\r
959                break;\r
960            }\r
961            buf.append(c);\r
962        }\r
963        // End of scan\r
964        if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) {\r
965            buf.append(SINGLE_QUOTE);\r
966        }\r
967        return new String(buf);\r
968    }\r
969    \r
970    /**\r
971     * Creates and returns a copy of this object.\r
972     *\r
973     * @return a clone of this instance.\r
974     * @stable ICU 3.0\r
975     */\r
976    public Object clone() {\r
977        return new MessageFormat((java.text.MessageFormat)messageFormat.clone());\r
978    }\r
979 \r
980    /**\r
981     * Equality comparison between two message format objects\r
982     * @stable ICU 3.0\r
983     */\r
984    public boolean equals(Object obj) {\r
985        try {\r
986            return messageFormat.equals(((MessageFormat)obj).messageFormat);\r
987        }\r
988        catch (Exception e) {\r
989            return false;\r
990        }\r
991    }\r
992 \r
993    /**\r
994     * Generates a hash code for the message format object.\r
995     * @stable ICU 3.0\r
996     */\r
997    public int hashCode() {\r
998        return messageFormat.hashCode();\r
999    }\r
1000 \r
1001    /**\r
1002     * Return a string suitable for debugging.\r
1003     * @return a string suitable for debugging\r
1004     * @stable ICU 3.4.2\r
1005     */\r
1006    public String toString() {\r
1007        return messageFormat.toPattern();\r
1008    }\r
1009 \r
1010    private static Format unwrap(Format f) {\r
1011        if (f instanceof DateFormat) {\r
1012            return ((DateFormat)f).dateFormat;\r
1013        } else if (f instanceof NumberFormat) {\r
1014            return ((NumberFormat)f).numberFormat;\r
1015        } else if (f instanceof MessageFormat) {\r
1016            return ((MessageFormat)f).messageFormat;\r
1017        } else {\r
1018            return f;\r
1019        }\r
1020    }\r
1021 \r
1022    private static Format wrap(Format f) {\r
1023        if (f instanceof java.text.DateFormat) {\r
1024            return new DateFormat((java.text.DateFormat)f);\r
1025        } else if (f instanceof java.text.DecimalFormat) {\r
1026            return new DecimalFormat((java.text.DecimalFormat)f);\r
1027        } else if (f instanceof java.text.MessageFormat) {\r
1028            return new MessageFormat((java.text.MessageFormat)f);\r
1029        } else {\r
1030            return f;\r
1031        }\r
1032    }\r
1033 \r
1034    private static final char SINGLE_QUOTE = '\'';\r
1035    private static final char CURLY_BRACE_LEFT = '{';\r
1036    private static final char CURLY_BRACE_RIGHT = '}';\r
1037 \r
1038    private static final int STATE_INITIAL = 0;\r
1039    private static final int STATE_SINGLE_QUOTE = 1;\r
1040    private static final int STATE_IN_QUOTE = 2;\r
1041    private static final int STATE_MSG_ELEMENT = 3;\r
1042 }\r