/* | |
******************************************************************************* | |
* Copyright (C) 2004-2006, International Business Machines Corporation and * | |
* others. All Rights Reserved. * | |
******************************************************************************* | |
*/ | |
package com.ibm.icu.text; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.text.FieldPosition; | |
import java.text.Format; | |
import java.text.ParseException; | |
import java.text.ParsePosition; | |
import java.util.Locale; | |
import com.ibm.icu.util.ULocale; | |
/** | |
* <code>MessageFormat</code> provides a means to produce concatenated | |
* messages in language-neutral way. Use this to construct messages | |
* displayed for end users. | |
* | |
* <p> | |
* <code>MessageFormat</code> takes a set of objects, formats them, then | |
* inserts the formatted strings into the pattern at the appropriate places. | |
* | |
* <p> | |
* <strong>Note:</strong> | |
* <code>MessageFormat</code> differs from the other <code>Format</code> | |
* classes in that you create a <code>MessageFormat</code> object with one | |
* of its constructors (not with a <code>getInstance</code> style factory | |
* method). The factory methods aren't necessary because <code>MessageFormat</code> | |
* itself doesn't implement locale specific behavior. Any locale specific | |
* behavior is defined by the pattern that you provide as well as the | |
* subformats used for inserted arguments. | |
* | |
* <h4><a name="patterns">Patterns and Their Interpretation</a></h4> | |
* | |
* <code>MessageFormat</code> uses patterns of the following form: | |
* <blockquote><pre> | |
* <i>MessageFormatPattern:</i> | |
* <i>String</i> | |
* <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i> | |
* | |
* <i>FormatElement:</i> | |
* { <i>ArgumentIndex</i> } | |
* { <i>ArgumentIndex</i> , <i>FormatType</i> } | |
* { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> } | |
* | |
* <i>FormatType: one of </i> | |
* number date time choice | |
* | |
* <i>FormatStyle:</i> | |
* short | |
* medium | |
* long | |
* full | |
* integer | |
* currency | |
* percent | |
* <i>SubformatPattern</i> | |
* | |
* <i>String:</i> | |
* <i>StringPart<sub>opt</sub></i> | |
* <i>String</i> <i>StringPart</i> | |
* | |
* <i>StringPart:</i> | |
* '' | |
* ' <i>QuotedString</i> ' | |
* <i>UnquotedString</i> | |
* | |
* <i>SubformatPattern:</i> | |
* <i>SubformatPatternPart<sub>opt</sub></i> | |
* <i>SubformatPattern</i> <i>SubformatPatternPart</i> | |
* | |
* <i>SubFormatPatternPart:</i> | |
* ' <i>QuotedPattern</i> ' | |
* <i>UnquotedPattern</i> | |
* </pre></blockquote> | |
* | |
* <p> | |
* Within a <i>String</i>, <code>"''"</code> represents a single | |
* quote. A <i>QuotedString</i> can contain arbitrary characters | |
* except single quotes; the surrounding single quotes are removed. | |
* An <i>UnquotedString</i> can contain arbitrary characters | |
* except single quotes and left curly brackets. Thus, a string that | |
* should result in the formatted message "'{0}'" can be written as | |
* <code>"'''{'0}''"</code> or <code>"'''{0}'''"</code>. | |
* <p> | |
* Within a <i>SubformatPattern</i>, different rules apply. | |
* A <i>QuotedPattern</i> can contain arbitrary characters | |
* except single quotes; but the surrounding single quotes are | |
* <strong>not</strong> removed, so they may be interpreted by the | |
* subformat. For example, <code>"{1,number,$'#',##}"</code> will | |
* produce a number format with the pound-sign quoted, with a result | |
* such as: "$#31,45". | |
* An <i>UnquotedPattern</i> can contain arbitrary characters | |
* except single quotes, but curly braces within it must be balanced. | |
* For example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code> | |
* are valid subformat patterns, but <code>"ab {0'}' de"</code> and | |
* <code>"ab } de"</code> are not. | |
* <p> | |
* <dl><dt><b>Warning:</b><dd>The rules for using quotes within message | |
* format patterns unfortunately have shown to be somewhat confusing. | |
* In particular, it isn't always obvious to localizers whether single | |
* quotes need to be doubled or not. Make sure to inform localizers about | |
* the rules, and tell them (for example, by using comments in resource | |
* bundle source files) which strings will be processed by MessageFormat. | |
* Note that localizers may need to use single quotes in translated | |
* strings where the original version doesn't have them. | |
* <br>Note also that the simplest way to avoid the problem is to | |
* use the real apostrophe (single quote) character \u2019 (') for | |
* human-readable text, and to use the ASCII apostrophe (\u0027 ' ) | |
* only in program syntax, like quoting in MessageFormat. | |
* See the annotations for U+0027 Apostrophe in The Unicode Standard.</p> | |
* </dl> | |
* <p> | |
* The <i>ArgumentIndex</i> value is a non-negative integer written | |
* using the digits '0' through '9', and represents an index into the | |
* <code>arguments</code> array passed to the <code>format</code> methods | |
* or the result array returned by the <code>parse</code> methods. | |
* <p> | |
* The <i>FormatType</i> and <i>FormatStyle</i> values are used to create | |
* a <code>Format</code> instance for the format element. The following | |
* table shows how the values map to Format instances. Combinations not | |
* shown in the table are illegal. A <i>SubformatPattern</i> must | |
* be a valid pattern string for the Format subclass used. | |
* <p> | |
* <table border=1> | |
* <tr> | |
* <th>Format Type | |
* <th>Format Style | |
* <th>Subformat Created | |
* <tr> | |
* <td colspan=2><i>(none)</i> | |
* <td><code>null</code> | |
* <tr> | |
* <td rowspan=5><code>number</code> | |
* <td><i>(none)</i> | |
* <td><code>NumberFormat.getInstance(getLocale())</code> | |
* <tr> | |
* <td><code>integer</code> | |
* <td><code>NumberFormat.getIntegerInstance(getLocale())</code> | |
* <tr> | |
* <td><code>currency</code> | |
* <td><code>NumberFormat.getCurrencyInstance(getLocale())</code> | |
* <tr> | |
* <td><code>percent</code> | |
* <td><code>NumberFormat.getPercentInstance(getLocale())</code> | |
* <tr> | |
* <td><i>SubformatPattern</i> | |
* <td><code>new DecimalFormat(subformatPattern, new DecimalFormatSymbols(getLocale()))</code> | |
* <tr> | |
* <td rowspan=6><code>date</code> | |
* <td><i>(none)</i> | |
* <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> | |
* <tr> | |
* <td><code>short</code> | |
* <td><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code> | |
* <tr> | |
* <td><code>medium</code> | |
* <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> | |
* <tr> | |
* <td><code>long</code> | |
* <td><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code> | |
* <tr> | |
* <td><code>full</code> | |
* <td><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code> | |
* <tr> | |
* <td><i>SubformatPattern</i> | |
* <td><code>new SimpleDateFormat(subformatPattern, getLocale()) | |
* <tr> | |
* <td rowspan=6><code>time</code> | |
* <td><i>(none)</i> | |
* <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> | |
* <tr> | |
* <td><code>short</code> | |
* <td><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code> | |
* <tr> | |
* <td><code>medium</code> | |
* <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> | |
* <tr> | |
* <td><code>long</code> | |
* <td><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code> | |
* <tr> | |
* <td><code>full</code> | |
* <td><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code> | |
* <tr> | |
* <td><i>SubformatPattern</i> | |
* <td><code>new SimpleDateFormat(subformatPattern, getLocale()) | |
* <tr> | |
* <td><code>choice</code> | |
* <td><i>SubformatPattern</i> | |
* <td><code>new ChoiceFormat(subformatPattern)</code> | |
* </table> | |
* <p> | |
* | |
* <h4>Usage Information</h4> | |
* | |
* <p> | |
* Here are some examples of usage: | |
* <blockquote> | |
* <pre> | |
* Object[] arguments = { | |
* new Integer(7), | |
* new Date(System.currentTimeMillis()), | |
* "a disturbance in the Force" | |
* }; | |
* | |
* String result = MessageFormat.format( | |
* "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", | |
* arguments); | |
* | |
* <em>output</em>: At 12:30 PM on Jul 3, 2053, there was a disturbance | |
* in the Force on planet 7. | |
* | |
* </pre> | |
* </blockquote> | |
* Typically, the message format will come from resources, and the | |
* arguments will be dynamically set at runtime. | |
* | |
* <p> | |
* Example 2: | |
* <blockquote> | |
* <pre> | |
* Object[] testArgs = {new Long(3), "MyDisk"}; | |
* | |
* MessageFormat form = new MessageFormat( | |
* "The disk \"{1}\" contains {0} file(s)."); | |
* | |
* System.out.println(form.format(testArgs)); | |
* | |
* // output, with different testArgs | |
* <em>output</em>: The disk "MyDisk" contains 0 file(s). | |
* <em>output</em>: The disk "MyDisk" contains 1 file(s). | |
* <em>output</em>: The disk "MyDisk" contains 1,273 file(s). | |
* </pre> | |
* </blockquote> | |
* | |
* <p> | |
* For more sophisticated patterns, you can use a <code>ChoiceFormat</code> to get | |
* output such as: | |
* <blockquote> | |
* <pre> | |
* MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}."); | |
* double[] filelimits = {0,1,2}; | |
* String[] filepart = {"no files","one file","{0,number} files"}; | |
* ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart); | |
* form.setFormatByArgumentIndex(0, fileform); | |
* | |
* Object[] testArgs = {new Long(12373), "MyDisk"}; | |
* | |
* System.out.println(form.format(testArgs)); | |
* | |
* // output, with different testArgs | |
* output: The disk "MyDisk" contains no files. | |
* output: The disk "MyDisk" contains one file. | |
* output: The disk "MyDisk" contains 1,273 files. | |
* </pre> | |
* </blockquote> | |
* You can either do this programmatically, as in the above example, | |
* or by using a pattern (see | |
* {@link ChoiceFormat} | |
* for more information) as in: | |
* <blockquote> | |
* <pre> | |
* form.applyPattern( | |
* "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}."); | |
* </pre> | |
* </blockquote> | |
* <p> | |
* <strong>Note:</strong> As we see above, the string produced | |
* by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated specially; | |
* occurances of '{' are used to indicated subformats, and cause recursion. | |
* If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code> | |
* programmatically (instead of using the string patterns), then be careful not to | |
* produce a format that recurses on itself, which will cause an infinite loop. | |
* <p> | |
* When a single argument is parsed more than once in the string, the last match | |
* will be the final result of the parsing. For example, | |
* <pre> | |
* MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}"); | |
* Object[] objs = {new Double(3.1415)}; | |
* String result = mf.format( objs ); | |
* // result now equals "3.14, 3.1" | |
* objs = null; | |
* objs = mf.parse(result, new ParsePosition(0)); | |
* // objs now equals {new Double(3.1)} | |
* </pre> | |
* <p> | |
* Likewise, parsing with a MessageFormat object using patterns containing | |
* multiple occurances of the same argument would return the last match. For | |
* example, | |
* <pre> | |
* MessageFormat mf = new MessageFormat("{0}, {0}, {0}"); | |
* String forParsing = "x, y, z"; | |
* Object[] objs = mf.parse(forParsing, new ParsePosition(0)); | |
* // result now equals {new String("z")} | |
* </pre> | |
* | |
* <h4><a name="synchronization">Synchronization</a></h4> | |
* | |
* <p> | |
* Message formats are not synchronized. | |
* It is recommended to create separate format instances for each thread. | |
* If multiple threads access a format concurrently, it must be synchronized | |
* externally. | |
* | |
* @see java.util.Locale | |
* @see Format | |
* @see NumberFormat | |
* @see DecimalFormat | |
* @see ChoiceFormat | |
* @author Mark Davis | |
* @stable ICU 3.0 | |
*/ | |
public class MessageFormat extends Format { | |
static final long serialVersionUID = 1L; | |
/** | |
* @internal | |
*/ | |
public final java.text.MessageFormat messageFormat; | |
/** | |
* @internal | |
* @param delegate the DateFormat to which to delegate | |
*/ | |
public MessageFormat(java.text.MessageFormat delegate) { | |
this.messageFormat = delegate; | |
} | |
/** | |
* Constructs a MessageFormat for the default locale and the | |
* specified pattern. | |
* The constructor first sets the locale, then parses the pattern and | |
* creates a list of subformats for the format elements contained in it. | |
* Patterns and their interpretation are specified in the | |
* <a href="#patterns">class description</a>. | |
* | |
* @param pattern the pattern for this message format | |
* @exception IllegalArgumentException if the pattern is invalid | |
* @stable ICU 3.0 | |
*/ | |
public MessageFormat(String pattern) { | |
this(new java.text.MessageFormat(pattern)); | |
} | |
/** | |
* Constructs a MessageFormat for the specified locale and | |
* pattern. | |
* The constructor first sets the locale, then parses the pattern and | |
* creates a list of subformats for the format elements contained in it. | |
* Patterns and their interpretation are specified in the | |
* <a href="#patterns">class description</a>. | |
* | |
* @param pattern the pattern for this message format | |
* @param locale the locale for this message format | |
* @exception IllegalArgumentException if the pattern is invalid | |
* @stable ICU 3.0 | |
*/ | |
public MessageFormat(String pattern, Locale locale) { | |
// locale is ignored | |
this(new java.text.MessageFormat(pattern)); | |
} | |
/** | |
* Constructs a MessageFormat for the specified locale and | |
* pattern. | |
* The constructor first sets the locale, then parses the pattern and | |
* creates a list of subformats for the format elements contained in it. | |
* Patterns and their interpretation are specified in the | |
* <a href="#patterns">class description</a>. | |
* | |
* @param pattern the pattern for this message format | |
* @param locale the locale for this message format | |
* @exception IllegalArgumentException if the pattern is invalid | |
* @stable ICU 3.2 | |
*/ | |
public MessageFormat(String pattern, ULocale locale) { | |
// locale is ignored | |
this(pattern); | |
} | |
/** | |
* Sets the locale to be used when creating or comparing subformats. | |
* This affects subsequent calls to the {@link #applyPattern applyPattern} | |
* and {@link #toPattern toPattern} methods as well as to the | |
* <code>format</code> and | |
* {@link #formatToCharacterIterator formatToCharacterIterator} methods. | |
* | |
* @param locale the locale to be used when creating or comparing subformats | |
* @stable ICU 3.0 | |
*/ | |
public void setLocale(Locale locale) { | |
messageFormat.setLocale(locale); | |
} | |
/** | |
* Sets the locale to be used when creating or comparing subformats. | |
* This affects subsequent calls to the {@link #applyPattern applyPattern} | |
* and {@link #toPattern toPattern} methods as well as to the | |
* <code>format</code> and | |
* {@link #formatToCharacterIterator formatToCharacterIterator} methods. | |
* | |
* @param locale the locale to be used when creating or comparing subformats | |
* @stable ICU 3.2 | |
*/ | |
public void setLocale(ULocale locale) { | |
messageFormat.setLocale(locale.toLocale()); | |
} | |
/** | |
* Gets the locale that's used when creating or comparing subformats. | |
* | |
* @return the locale used when creating or comparing subformats | |
* @stable ICU 3.0 | |
*/ | |
public Locale getLocale() { | |
return messageFormat.getLocale(); | |
} | |
/** | |
* Gets the locale that's used when creating or comparing subformats. | |
* | |
* @return the locale used when creating or comparing subformats | |
* @stable ICU 3.2 | |
*/ | |
public ULocale getULocale() { | |
return ULocale.forLocale(messageFormat.getLocale()); | |
} | |
/** | |
* Sets the pattern used by this message format. | |
* The method parses the pattern and creates a list of subformats | |
* for the format elements contained in it. | |
* Patterns and their interpretation are specified in the | |
* <a href="#patterns">class description</a>. | |
* | |
* @param pattern the pattern for this message format | |
* @exception IllegalArgumentException if the pattern is invalid | |
* @stable ICU 3.0 | |
*/ | |
public void applyPattern(String pattern) { | |
messageFormat.applyPattern(pattern); | |
} | |
/** | |
* Returns a pattern representing the current state of the message format. | |
* The string is constructed from internal information and therefore | |
* does not necessarily equal the previously applied pattern. | |
* | |
* @return a pattern representing the current state of the message format | |
* @stable ICU 3.0 | |
*/ | |
public String toPattern() { | |
return messageFormat.toPattern(); | |
} | |
/** | |
* Sets the formats to use for the values passed into | |
* <code>format</code> methods or returned from <code>parse</code> | |
* methods. The indices of elements in <code>newFormats</code> | |
* correspond to the argument indices used in the previously set | |
* pattern string. | |
* The order of formats in <code>newFormats</code> thus corresponds to | |
* the order of elements in the <code>arguments</code> array passed | |
* to the <code>format</code> methods or the result array returned | |
* by the <code>parse</code> methods. | |
* <p> | |
* If an argument index is used for more than one format element | |
* in the pattern string, then the corresponding new format is used | |
* for all such format elements. If an argument index is not used | |
* for any format element in the pattern string, then the | |
* corresponding new format is ignored. If fewer formats are provided | |
* than needed, then only the formats for argument indices less | |
* than <code>newFormats.length</code> are replaced. | |
* | |
* @param newFormats the new formats to use | |
* @exception NullPointerException if <code>newFormats</code> is null | |
* @stable ICU 3.0 | |
* @throws UnsupportedOperationException if the underlying JVM does not | |
* support this method. | |
*/ | |
public void setFormatsByArgumentIndex(Format[] newFormats) { | |
if (sfsbai == null) { | |
synchronized (missing) { | |
try { | |
Class[] params = { Format[].class }; | |
sfsbai = java.text.MessageFormat.class.getMethod("setFormatsByArgumentIndex", params); | |
} | |
catch (NoSuchMethodException e) { | |
sfsbai = missing; | |
} | |
} | |
} | |
if (sfsbai != missing) { | |
try { | |
Format[] unwrapped = new Format[newFormats.length]; | |
for (int i = 0; i < newFormats.length; ++i) { | |
unwrapped[i] = unwrap(newFormats[i]); | |
} | |
Object[] args = { unwrapped }; | |
((Method)sfsbai).invoke(messageFormat, args); | |
return; | |
} | |
catch (IllegalAccessException e) { | |
// can't happen | |
} | |
catch (InvocationTargetException e) { | |
// can't happen | |
} | |
} | |
throw new UnsupportedOperationException(); | |
} | |
private static Object sfsbai; | |
/** | |
* Sets the formats to use for the format elements in the | |
* previously set pattern string. | |
* The order of formats in <code>newFormats</code> corresponds to | |
* the order of format elements in the pattern string. | |
* <p> | |
* If more formats are provided than needed by the pattern string, | |
* the remaining ones are ignored. If fewer formats are provided | |
* than needed, then only the first <code>newFormats.length</code> | |
* formats are replaced. | |
* <p> | |
* Since the order of format elements in a pattern string often | |
* changes during localization, it is generally better to use the | |
* {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex} | |
* method, which assumes an order of formats corresponding to the | |
* order of elements in the <code>arguments</code> array passed to | |
* the <code>format</code> methods or the result array returned by | |
* the <code>parse</code> methods. | |
* | |
* @param newFormats the new formats to use | |
* @exception NullPointerException if <code>newFormats</code> is null | |
* @stable ICU 3.0 | |
*/ | |
public void setFormats(Format[] newFormats) { | |
messageFormat.setFormats(newFormats); | |
} | |
/** | |
* Sets the format to use for the format elements within the | |
* previously set pattern string that use the given argument | |
* index. | |
* The argument index is part of the format element definition and | |
* represents an index into the <code>arguments</code> array passed | |
* to the <code>format</code> methods or the result array returned | |
* by the <code>parse</code> methods. | |
* <p> | |
* If the argument index is used for more than one format element | |
* in the pattern string, then the new format is used for all such | |
* format elements. If the argument index is not used for any format | |
* element in the pattern string, then the new format is ignored. | |
* | |
* @param argumentIndex the argument index for which to use the new format | |
* @param newFormat the new format to use | |
* @stable ICU 3.0 | |
* @throws UnsupportedOperationException if the underlying JVM does not | |
* support this method. | |
*/ | |
public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) { | |
if (sfbai == null) { | |
synchronized (missing) { | |
try { | |
Class[] params = { Integer.TYPE, Format.class }; | |
sfbai = java.text.MessageFormat.class.getMethod("setFormatByArgumentIndex", params); | |
} | |
catch (NoSuchMethodException e) { | |
sfbai = missing; | |
} | |
} | |
} | |
if (sfbai != missing) { | |
try { | |
Object[] args = { new Integer(argumentIndex), newFormat }; | |
((Method)sfbai).invoke(messageFormat, args); | |
return; | |
} | |
catch (IllegalAccessException e) { | |
// can't happen | |
} | |
catch (InvocationTargetException e) { | |
// can't happen | |
} | |
} | |
throw new UnsupportedOperationException(); | |
} | |
private static Object sfbai; | |
/** | |
* Sets the format to use for the format element with the given | |
* format element index within the previously set pattern string. | |
* The format element index is the zero-based number of the format | |
* element counting from the start of the pattern string. | |
* <p> | |
* Since the order of format elements in a pattern string often | |
* changes during localization, it is generally better to use the | |
* {@link #setFormatByArgumentIndex setFormatByArgumentIndex} | |
* method, which accesses format elements based on the argument | |
* index they specify. | |
* | |
* @param formatElementIndex the index of a format element within the pattern | |
* @param newFormat the format to use for the specified format element | |
* @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or | |
* larger than the number of format elements in the pattern string | |
* @stable ICU 3.0 | |
*/ | |
public void setFormat(int formatElementIndex, Format newFormat) { | |
messageFormat.setFormat(formatElementIndex, unwrap(newFormat)); | |
} | |
/** | |
* Gets the formats used for the values passed into | |
* <code>format</code> methods or returned from <code>parse</code> | |
* methods. The indices of elements in the returned array | |
* correspond to the argument indices used in the previously set | |
* pattern string. | |
* The order of formats in the returned array thus corresponds to | |
* the order of elements in the <code>arguments</code> array passed | |
* to the <code>format</code> methods or the result array returned | |
* by the <code>parse</code> methods. | |
* <p> | |
* If an argument index is used for more than one format element | |
* in the pattern string, then the format used for the last such | |
* format element is returned in the array. If an argument index | |
* is not used for any format element in the pattern string, then | |
* null is returned in the array. | |
* | |
* @return the formats used for the arguments within the pattern | |
* @stable ICU 3.0 | |
* @throws UnsupportedOperationException if the underlying JVM does not | |
* support this method. | |
*/ | |
public Format[] getFormatsByArgumentIndex() { | |
if (gfbai == null) { | |
synchronized (missing) { | |
try { | |
gfbai = java.text.MessageFormat.class.getMethod("getFormatsByArgumentIndex", null); | |
} | |
catch (NoSuchMethodException e) { | |
gfbai = missing; | |
} | |
} | |
} | |
if (gfbai != missing) { | |
try { | |
Format[] result = (Format[])((Method)gfbai).invoke(messageFormat, null); | |
for (int i = 0; i < result.length; ++i) { | |
result[i] = wrap(result[i]); | |
} | |
return result; | |
} | |
catch (IllegalAccessException e) { | |
// can't happen | |
} | |
catch (InvocationTargetException e) { | |
// can't happen | |
} | |
} | |
throw new UnsupportedOperationException(); | |
} | |
private static Object gfbai; | |
private static final Object missing = new Object(); | |
/** | |
* Gets the formats used for the format elements in the | |
* previously set pattern string. | |
* The order of formats in the returned array corresponds to | |
* the order of format elements in the pattern string. | |
* <p> | |
* Since the order of format elements in a pattern string often | |
* changes during localization, it's generally better to use the | |
* {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex} | |
* method, which assumes an order of formats corresponding to the | |
* order of elements in the <code>arguments</code> array passed to | |
* the <code>format</code> methods or the result array returned by | |
* the <code>parse</code> methods. | |
* | |
* @return the formats used for the format elements in the pattern | |
* @stable ICU 3.0 | |
*/ | |
public Format[] getFormats() { | |
Format[] result = messageFormat.getFormats(); | |
for (int i = 0; i < result.length; ++i) { | |
result[i] = wrap(result[i]); | |
} | |
return result; | |
} | |
/** | |
* Formats an array of objects and appends the <code>MessageFormat</code>'s | |
* pattern, with format elements replaced by the formatted objects, to the | |
* provided <code>StringBuffer</code>. | |
* <p> | |
* The text substituted for the individual format elements is derived from | |
* the current subformat of the format element and the | |
* <code>arguments</code> element at the format element's argument index | |
* as indicated by the first matching line of the following table. An | |
* argument is <i>unavailable</i> if <code>arguments</code> is | |
* <code>null</code> or has fewer than argumentIndex+1 elements. | |
* <p> | |
* <table border=1> | |
* <tr> | |
* <th>Subformat | |
* <th>Argument | |
* <th>Formatted Text | |
* <tr> | |
* <td><i>any</i> | |
* <td><i>unavailable</i> | |
* <td><code>"{" + argumentIndex + "}"</code> | |
* <tr> | |
* <td><i>any</i> | |
* <td><code>null</code> | |
* <td><code>"null"</code> | |
* <tr> | |
* <td><code>instanceof ChoiceFormat</code> | |
* <td><i>any</i> | |
* <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br> | |
* (new MessageFormat(subformat.format(argument), getLocale())).format(argument) : | |
* subformat.format(argument)</code> | |
* <tr> | |
* <td><code>!= null</code> | |
* <td><i>any</i> | |
* <td><code>subformat.format(argument)</code> | |
* <tr> | |
* <td><code>null</code> | |
* <td><code>instanceof Number</code> | |
* <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code> | |
* <tr> | |
* <td><code>null</code> | |
* <td><code>instanceof Date</code> | |
* <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code> | |
* <tr> | |
* <td><code>null</code> | |
* <td><code>instanceof String</code> | |
* <td><code>argument</code> | |
* <tr> | |
* <td><code>null</code> | |
* <td><i>any</i> | |
* <td><code>argument.toString()</code> | |
* </table> | |
* <p> | |
* If <code>pos</code> is non-null, and refers to | |
* <code>Field.ARGUMENT</code>, the location of the first formatted | |
* string will be returned. | |
* | |
* @param arguments an array of objects to be formatted and substituted. | |
* @param result where text is appended. | |
* @param pos On input: an alignment field, if desired. | |
* On output: the offsets of the alignment field. | |
* @exception IllegalArgumentException if an argument in the | |
* <code>arguments</code> array is not of the type | |
* expected by the format element(s) that use it. | |
* @stable ICU 3.0 | |
*/ | |
public final StringBuffer format(Object[] arguments, StringBuffer result, | |
FieldPosition pos) | |
{ | |
return messageFormat.format(arguments, result, pos); | |
} | |
/** | |
* Creates a MessageFormat with the given pattern and uses it | |
* to format the given arguments. This is equivalent to | |
* <blockquote> | |
* <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> | |
* </blockquote> | |
* | |
* @exception IllegalArgumentException if the pattern is invalid, | |
* or if an argument in the <code>arguments</code> array | |
* is not of the type expected by the format element(s) | |
* that use it. | |
* @stable ICU 3.0 | |
*/ | |
public static String format(String pattern, Object[] arguments) { | |
return java.text.MessageFormat.format(pattern, arguments); | |
} | |
// Overrides | |
/** | |
* Formats an array of objects and appends the <code>MessageFormat</code>'s | |
* pattern, with format elements replaced by the formatted objects, to the | |
* provided <code>StringBuffer</code>. | |
* This is equivalent to | |
* <blockquote> | |
* <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code> | |
* </blockquote> | |
* | |
* @param arguments an array of objects to be formatted and substituted. | |
* @param result where text is appended. | |
* @param pos On input: an alignment field, if desired. | |
* On output: the offsets of the alignment field. | |
* @exception IllegalArgumentException if an argument in the | |
* <code>arguments</code> array is not of the type | |
* expected by the format element(s) that use it. | |
* @stable ICU 3.0 | |
*/ | |
public final StringBuffer format(Object arguments, StringBuffer result, | |
FieldPosition pos) | |
{ | |
return messageFormat.format(arguments, result, pos); | |
} | |
/** | |
* Parses the string. | |
* | |
* <p>Caveats: The parse may fail in a number of circumstances. | |
* For example: | |
* <ul> | |
* <li>If one of the arguments does not occur in the pattern. | |
* <li>If the format of an argument loses information, such as | |
* with a choice format where a large number formats to "many". | |
* <li>Does not yet handle recursion (where | |
* the substituted strings contain {n} references.) | |
* <li>Will not always find a match (or the correct match) | |
* if some part of the parse is ambiguous. | |
* For example, if the pattern "{1},{2}" is used with the | |
* string arguments {"a,b", "c"}, it will format as "a,b,c". | |
* When the result is parsed, it will return {"a", "b,c"}. | |
* <li>If a single argument is parsed more than once in the string, | |
* then the later parse wins. | |
* </ul> | |
* When the parse fails, use ParsePosition.getErrorIndex() to find out | |
* where in the string did the parsing failed. The returned error | |
* index is the starting offset of the sub-patterns that the string | |
* is comparing with. For example, if the parsing string "AAA {0} BBB" | |
* is comparing against the pattern "AAD {0} BBB", the error index is | |
* 0. When an error occurs, the call to this method will return null. | |
* If the source is null, return an empty array. | |
* @stable ICU 3.0 | |
*/ | |
public Object[] parse(String source, ParsePosition pos) { | |
return messageFormat.parse(source, pos); | |
} | |
/** | |
* Parses text from the beginning of the given string to produce an object | |
* array. | |
* The method may not use the entire text of the given string. | |
* <p> | |
* See the {@link #parse(String, ParsePosition)} method for more information | |
* on message parsing. | |
* | |
* @param source A <code>String</code> whose beginning should be parsed. | |
* @return An <code>Object</code> array parsed from the string. | |
* @exception ParseException if the beginning of the specified string | |
* cannot be parsed. | |
* @stable ICU 3.0 | |
*/ | |
public Object[] parse(String source) throws ParseException { | |
return messageFormat.parse(source); | |
} | |
/** | |
* Parses text from a string to produce an object array. | |
* <p> | |
* The method attempts to parse text starting at the index given by | |
* <code>pos</code>. | |
* If parsing succeeds, then the index of <code>pos</code> is updated | |
* to the index after the last character used (parsing does not necessarily | |
* use all characters up to the end of the string), and the parsed | |
* object array is returned. The updated <code>pos</code> can be used to | |
* indicate the starting point for the next call to this method. | |
* If an error occurs, then the index of <code>pos</code> is not | |
* changed, the error index of <code>pos</code> is set to the index of | |
* the character where the error occurred, and null is returned. | |
* <p> | |
* See the {@link #parse(String, ParsePosition)} method for more information | |
* on message parsing. | |
* | |
* @param source A <code>String</code>, part of which should be parsed. | |
* @param pos A <code>ParsePosition</code> object with index and error | |
* index information as described above. | |
* @return An <code>Object</code> array parsed from the string. In case of | |
* error, returns null. | |
* @exception NullPointerException if <code>pos</code> is null. | |
* @stable ICU 3.0 | |
*/ | |
public Object parseObject(String source, ParsePosition pos) { | |
return messageFormat.parse(source, pos); | |
} | |
/** | |
* Convert an 'apostrophe-friendly' pattern into a standard | |
* pattern. Standard patterns treat all apostrophes as | |
* quotes, which is problematic in some languages, e.g. | |
* French, where apostrophe is commonly used. This utility | |
* assumes that only an unpaired apostrophe immediately before | |
* a brace is a true quote. Other unpaired apostrophes are paired, | |
* and the resulting standard pattern string is returned. | |
* | |
* <p><b>Note</b> it is not guaranteed that the returned pattern | |
* is indeed a valid pattern. The only effect is to convert | |
* between patterns having different quoting semantics. | |
* | |
* @param pattern the 'apostrophe-friendly' patttern to convert | |
* @return the standard equivalent of the original pattern | |
* @draft ICU 3.4 | |
* @provisional | |
*/ | |
public static String autoQuoteApostrophe(String pattern) { | |
StringBuffer buf = new StringBuffer(pattern.length()*2); | |
int state = STATE_INITIAL; | |
int braceCount = 0; | |
for (int i = 0, j = pattern.length(); i < j; ++i) { | |
char c = pattern.charAt(i); | |
switch (state) { | |
case STATE_INITIAL: | |
switch (c) { | |
case SINGLE_QUOTE: | |
state = STATE_SINGLE_QUOTE; | |
break; | |
case CURLY_BRACE_LEFT: | |
state = STATE_MSG_ELEMENT; | |
++braceCount; | |
break; | |
} | |
break; | |
case STATE_SINGLE_QUOTE: | |
switch (c) { | |
case SINGLE_QUOTE: | |
state = STATE_INITIAL; | |
break; | |
case CURLY_BRACE_LEFT: | |
case CURLY_BRACE_RIGHT: | |
state = STATE_IN_QUOTE; | |
break; | |
default: | |
buf.append(SINGLE_QUOTE); | |
state = STATE_INITIAL; | |
break; | |
} | |
break; | |
case STATE_IN_QUOTE: | |
switch (c) { | |
case SINGLE_QUOTE: | |
state = STATE_INITIAL; | |
break; | |
} | |
break; | |
case STATE_MSG_ELEMENT: | |
switch (c) { | |
case CURLY_BRACE_LEFT: | |
++braceCount; | |
break; | |
case CURLY_BRACE_RIGHT: | |
if (--braceCount == 0) { | |
state = STATE_INITIAL; | |
} | |
break; | |
} | |
break; | |
default: // Never happens. | |
break; | |
} | |
buf.append(c); | |
} | |
// End of scan | |
if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) { | |
buf.append(SINGLE_QUOTE); | |
} | |
return new String(buf); | |
} | |
/** | |
* Creates and returns a copy of this object. | |
* | |
* @return a clone of this instance. | |
* @stable ICU 3.0 | |
*/ | |
public Object clone() { | |
return new MessageFormat((java.text.MessageFormat)messageFormat.clone()); | |
} | |
/** | |
* Equality comparison between two message format objects | |
* @stable ICU 3.0 | |
*/ | |
public boolean equals(Object obj) { | |
try { | |
return messageFormat.equals(((MessageFormat)obj).messageFormat); | |
} | |
catch (Exception e) { | |
return false; | |
} | |
} | |
/** | |
* Generates a hash code for the message format object. | |
* @stable ICU 3.0 | |
*/ | |
public int hashCode() { | |
return messageFormat.hashCode(); | |
} | |
/** | |
* Return a string suitable for debugging. | |
* @return a string suitable for debugging | |
* @draft ICU 3.4.2 | |
*/ | |
public String toString() { | |
return messageFormat.toPattern(); | |
} | |
private static Format unwrap(Format f) { | |
if (f instanceof DateFormat) { | |
return ((DateFormat)f).dateFormat; | |
} else if (f instanceof NumberFormat) { | |
return ((NumberFormat)f).numberFormat; | |
} else if (f instanceof MessageFormat) { | |
return ((MessageFormat)f).messageFormat; | |
} else { | |
return f; | |
} | |
} | |
private static Format wrap(Format f) { | |
if (f instanceof java.text.DateFormat) { | |
return new DateFormat((java.text.DateFormat)f); | |
} else if (f instanceof java.text.DecimalFormat) { | |
return new DecimalFormat((java.text.DecimalFormat)f); | |
} else if (f instanceof java.text.MessageFormat) { | |
return new MessageFormat((java.text.MessageFormat)f); | |
} else { | |
return f; | |
} | |
} | |
private static final char SINGLE_QUOTE = '\''; | |
private static final char CURLY_BRACE_LEFT = '{'; | |
private static final char CURLY_BRACE_RIGHT = '}'; | |
private static final int STATE_INITIAL = 0; | |
private static final int STATE_SINGLE_QUOTE = 1; | |
private static final int STATE_IN_QUOTE = 2; | |
private static final int STATE_MSG_ELEMENT = 3; | |
} |