/* * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved * * The original version of this source code and documentation is copyrighted * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These * materials are provided under terms of a License Agreement between Taligent * and Sun. This technology is protected by multiple US and International * patents. This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. * */ package java.text; import java.io.InvalidObjectException;import java.io.IOException;import java.io.ObjectInputStream;import java.text.DecimalFormat;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.List;import java.util.Locale; /** * <code>MessageFormat</code> provides a means to produce concatenated * messages in a 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> * </pre></blockquote> * * <p>Within a <i>String</i>, a pair of single quotes can be used to * quote any arbitrary characters except single quotes. For example, * pattern string <code>"'{0}'"</code> represents string * <code>"{0}"</code>, not a <i>FormatElement</i>. A single quote itself * must be represented by doubled single quotes {@code ''} throughout a * <i>String</i>. For example, pattern string <code>"'{''}'"</code> is * interpreted as a sequence of <code>'{</code> (start of quoting and a * left curly brace), <code>''</code> (a single quote), and * <code>}'</code> (a right curly brace and end of quoting), * <em>not</em> <code>'{'</code> and <code>'}'</code> (quoted left and * right curly braces): representing string <code>"{'}"</code>, * <em>not</em> <code>"{}"</code>. * * <p>A <i>SubformatPattern</i> is interpreted by its corresponding * subformat, and subformat-dependent pattern rules apply. For example, * pattern string <code>"{1,number,<u>$'#',##</u>}"</code> * (<i>SubformatPattern</i> with underline) will produce a number format * with the pound-sign quoted, with a result such as: {@code * "$#31,45"}. Refer to each {@code Format} subclass documentation for * details. * * <p>Any unmatched quote is treated as closed at the end of the given * pattern. For example, pattern string {@code "'{0}"} is treated as * pattern {@code "'{0}'"}. * * <p>Any curly braces within an unquoted pattern must be balanced. For * example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code> are * valid patterns, but <code>"ab {0'}' de"</code>, <code>"ab } de"</code> * and <code>"''{''"</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 {@code MessageFormat}. * Note that localizers may need to use single quotes in translated * strings where the original version doesn't have them. * </dl> * <p> * The <i>ArgumentIndex</i> value is a non-negative integer written * using the digits {@code '0'} through {@code '9'}, and represents an index into the * {@code arguments} array passed to the {@code format} methods * or the result array returned by the {@code parse} methods. * <p> * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create * a {@code Format} instance for the format element. The following * table shows how the values map to {@code Format} instances. Combinations not * shown in the table are illegal. A <i>SubformatPattern</i> must * be a valid pattern string for the {@code Format} subclass used. * <p> * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances"> * <tr> * <th id="ft" class="TableHeadingColor">FormatType * <th id="fs" class="TableHeadingColor">FormatStyle * <th id="sc" class="TableHeadingColor">Subformat Created * <tr> * <td headers="ft"><i>(none)</i> * <td headers="fs"><i>(none)</i> * <td headers="sc"><code>null</code> * <tr> * <td headers="ft" rowspan=5><code>number</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>integer</code> * <td headers="sc">{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>currency</code> * <td headers="sc">{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>percent</code> * <td headers="sc">{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))} * <tr> * <td headers="ft" rowspan=6><code>date</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>short</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())} * <tr> * <td headers="fs"><code>medium</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>long</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())} * <tr> * <td headers="fs"><code>full</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())} * <tr> * <td headers="ft" rowspan=6><code>time</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>short</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())} * <tr> * <td headers="fs"><code>medium</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>long</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())} * <tr> * <td headers="fs"><code>full</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())} * <tr> * <td headers="ft"><code>choice</code> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)} * </table> * <p> * * <h4>Usage Information</h4> * * <p> * Here are some examples of usage. * In real internationalized programs, the message format pattern and other * static strings will, of course, be obtained from resource bundles. * Other parameters will be dynamically determined at runtime. * <p> * The first example uses the static method <code>MessageFormat.format</code>, * which internally creates a <code>MessageFormat</code> for one-time use: * <blockquote><pre> * int planet = 7; * String event = "a disturbance in the Force"; * * String result = MessageFormat.format( * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", * planet, new Date(), event); * </pre></blockquote> * The output is: * <blockquote><pre> * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7. * </pre></blockquote> * * <p> * The following example creates a <code>MessageFormat</code> instance that * can be used repeatedly: * <blockquote><pre> * int fileCount = 1273; * String diskName = "MyDisk"; * Object[] testArgs = {new Long(fileCount), diskName}; * * MessageFormat form = new MessageFormat( * "The disk \"{1}\" contains {0} file(s)."); * * System.out.println(form.format(testArgs)); * </pre></blockquote> * The output with different values for <code>fileCount</code>: * <blockquote><pre> * The disk "MyDisk" contains 0 file(s). * The disk "MyDisk" contains 1 file(s). * The disk "MyDisk" contains 1,273 file(s). * </pre></blockquote> * * <p> * For more sophisticated patterns, you can use a <code>ChoiceFormat</code> * to produce correct forms for singular and plural: * <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); * * int fileCount = 1273; * String diskName = "MyDisk"; * Object[] testArgs = {new Long(fileCount), diskName}; * * System.out.println(form.format(testArgs)); * </pre></blockquote> * The output with different values for <code>fileCount</code>: * <blockquote><pre> * The disk "MyDisk" contains no files. * The disk "MyDisk" contains one file. * The disk "MyDisk" contains 1,273 files. * </pre></blockquote> * * <p> * You can create the <code>ChoiceFormat</code> programmatically, as in the * above example, or by using a pattern. See {@link ChoiceFormat} * for more information. * <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 as special; * occurrences of '{' are used to indicate 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, * <blockquote><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></blockquote> * * <p> * Likewise, parsing with a {@code MessageFormat} object using patterns containing * multiple occurrences of the same argument would return the last match. For * example, * <blockquote><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></blockquote> * * <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 DecimalFormatSymbols * @see ChoiceFormat * @see DateFormat * @see SimpleDateFormat * * @author Mark Davis */ public class MessageFormat extends Format { private static final long serialVersionUID = 6479157306784022952L; /** * 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 */ public MessageFormat(String pattern) { this.locale = Locale.getDefault(Locale.Category.FORMAT); applyPattern(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 * @since 1.4 */ public MessageFormat(String pattern, Locale locale) { this.locale = locale; applyPattern(pattern); } /** * Sets the locale to be used when creating or comparing subformats. * This affects subsequent calls * <ul> * <li>to the {@link #applyPattern applyPattern} * and {@link #toPattern toPattern} methods if format elements specify * a format type and therefore have the subformats created in the * <code>applyPattern</code> method, as well as * <li>to the <code>format</code> and * {@link #formatToCharacterIterator formatToCharacterIterator} methods * if format elements do not specify a format type and therefore have * the subformats created in the formatting methods. * </ul> * Subformats that have already been created are not affected. * * @param locale the locale to be used when creating or comparing subformats */ public void setLocale(Locale locale) { this.locale = locale; } /** * Gets the locale that's used when creating or comparing subformats. * * @return the locale used when creating or comparing subformats */ public Locale getLocale() { return locale; } /** * 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 */ public void applyPattern(String pattern) { StringBuilder[] segments = new StringBuilder[4]; // Allocate only segments[SEG_RAW] here. The rest are // allocated on demand. segments[SEG_RAW] = new StringBuilder(); int part = SEG_RAW; int formatNumber = 0; boolean inQuote = false; int braceStack = 0; maxOffset = -1; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (part == SEG_RAW) { if (ch == '\'') { if (i + 1 < pattern.length() && pattern.charAt(i+1) == '\'') { segments[part].append(ch); // handle doubles ++i; } else { inQuote = !inQuote; } } else if (ch == '{' && !inQuote) { part = SEG_INDEX; if (segments[SEG_INDEX] == null) { segments[SEG_INDEX] = new StringBuilder(); } } else { segments[part].append(ch); } } else { if (inQuote) { // just copy quotes in parts segments[part].append(ch); if (ch == '\'') { inQuote = false; } } else { switch (ch) { case ',': if (part < SEG_MODIFIER) { if (segments[++part] == null) { segments[part] = new StringBuilder(); } } else { segments[part].append(ch); } break; case '{': ++braceStack; segments[part].append(ch); break; case '}': if (braceStack == 0) { part = SEG_RAW; makeFormat(i, formatNumber, segments); formatNumber++; // throw away other segments segments[SEG_INDEX] = null; segments[SEG_TYPE] = null; segments[SEG_MODIFIER] = null; } else { --braceStack; segments[part].append(ch); } break; case ' ': // Skip any leading space chars for SEG_TYPE. if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) { segments[part].append(ch); } break; case '\'': inQuote = true; // fall through, so we keep quotes in other parts default: segments[part].append(ch);Lines 1–500 of 1594. Load more
/* * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved * * The original version of this source code and documentation is copyrighted * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These * materials are provided under terms of a License Agreement between Taligent * and Sun. This technology is protected by multiple US and International * patents. This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. * */ package java.text; import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; /** * <code>MessageFormat</code> provides a means to produce concatenated * messages in a 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> * </pre></blockquote> * * <p>Within a <i>String</i>, a pair of single quotes can be used to * quote any arbitrary characters except single quotes. For example, * pattern string <code>"'{0}'"</code> represents string * <code>"{0}"</code>, not a <i>FormatElement</i>. A single quote itself * must be represented by doubled single quotes {@code ''} throughout a * <i>String</i>. For example, pattern string <code>"'{''}'"</code> is * interpreted as a sequence of <code>'{</code> (start of quoting and a * left curly brace), <code>''</code> (a single quote), and * <code>}'</code> (a right curly brace and end of quoting), * <em>not</em> <code>'{'</code> and <code>'}'</code> (quoted left and * right curly braces): representing string <code>"{'}"</code>, * <em>not</em> <code>"{}"</code>. * * <p>A <i>SubformatPattern</i> is interpreted by its corresponding * subformat, and subformat-dependent pattern rules apply. For example, * pattern string <code>"{1,number,<u>$'#',##</u>}"</code> * (<i>SubformatPattern</i> with underline) will produce a number format * with the pound-sign quoted, with a result such as: {@code * "$#31,45"}. Refer to each {@code Format} subclass documentation for * details. * * <p>Any unmatched quote is treated as closed at the end of the given * pattern. For example, pattern string {@code "'{0}"} is treated as * pattern {@code "'{0}'"}. * * <p>Any curly braces within an unquoted pattern must be balanced. For * example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code> are * valid patterns, but <code>"ab {0'}' de"</code>, <code>"ab } de"</code> * and <code>"''{''"</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 {@code MessageFormat}. * Note that localizers may need to use single quotes in translated * strings where the original version doesn't have them. * </dl> * <p> * The <i>ArgumentIndex</i> value is a non-negative integer written * using the digits {@code '0'} through {@code '9'}, and represents an index into the * {@code arguments} array passed to the {@code format} methods * or the result array returned by the {@code parse} methods. * <p> * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create * a {@code Format} instance for the format element. The following * table shows how the values map to {@code Format} instances. Combinations not * shown in the table are illegal. A <i>SubformatPattern</i> must * be a valid pattern string for the {@code Format} subclass used. * <p> * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances"> * <tr> * <th id="ft" class="TableHeadingColor">FormatType * <th id="fs" class="TableHeadingColor">FormatStyle * <th id="sc" class="TableHeadingColor">Subformat Created * <tr> * <td headers="ft"><i>(none)</i> * <td headers="fs"><i>(none)</i> * <td headers="sc"><code>null</code> * <tr> * <td headers="ft" rowspan=5><code>number</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>integer</code> * <td headers="sc">{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>currency</code> * <td headers="sc">{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())} * <tr> * <td headers="fs"><code>percent</code> * <td headers="sc">{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))} * <tr> * <td headers="ft" rowspan=6><code>date</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>short</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())} * <tr> * <td headers="fs"><code>medium</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>long</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())} * <tr> * <td headers="fs"><code>full</code> * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())} * <tr> * <td headers="ft" rowspan=6><code>time</code> * <td headers="fs"><i>(none)</i> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>short</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())} * <tr> * <td headers="fs"><code>medium</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())} * <tr> * <td headers="fs"><code>long</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())} * <tr> * <td headers="fs"><code>full</code> * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())} * <tr> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())} * <tr> * <td headers="ft"><code>choice</code> * <td headers="fs"><i>SubformatPattern</i> * <td headers="sc">{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)} * </table> * <p> * * <h4>Usage Information</h4> * * <p> * Here are some examples of usage. * In real internationalized programs, the message format pattern and other * static strings will, of course, be obtained from resource bundles. * Other parameters will be dynamically determined at runtime. * <p> * The first example uses the static method <code>MessageFormat.format</code>, * which internally creates a <code>MessageFormat</code> for one-time use: * <blockquote><pre> * int planet = 7; * String event = "a disturbance in the Force"; * * String result = MessageFormat.format( * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", * planet, new Date(), event); * </pre></blockquote> * The output is: * <blockquote><pre> * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7. * </pre></blockquote> * * <p> * The following example creates a <code>MessageFormat</code> instance that * can be used repeatedly: * <blockquote><pre> * int fileCount = 1273; * String diskName = "MyDisk"; * Object[] testArgs = {new Long(fileCount), diskName}; * * MessageFormat form = new MessageFormat( * "The disk \"{1}\" contains {0} file(s)."); * * System.out.println(form.format(testArgs)); * </pre></blockquote> * The output with different values for <code>fileCount</code>: * <blockquote><pre> * The disk "MyDisk" contains 0 file(s). * The disk "MyDisk" contains 1 file(s). * The disk "MyDisk" contains 1,273 file(s). * </pre></blockquote> * * <p> * For more sophisticated patterns, you can use a <code>ChoiceFormat</code> * to produce correct forms for singular and plural: * <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); * * int fileCount = 1273; * String diskName = "MyDisk"; * Object[] testArgs = {new Long(fileCount), diskName}; * * System.out.println(form.format(testArgs)); * </pre></blockquote> * The output with different values for <code>fileCount</code>: * <blockquote><pre> * The disk "MyDisk" contains no files. * The disk "MyDisk" contains one file. * The disk "MyDisk" contains 1,273 files. * </pre></blockquote> * * <p> * You can create the <code>ChoiceFormat</code> programmatically, as in the * above example, or by using a pattern. See {@link ChoiceFormat} * for more information. * <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 as special; * occurrences of '{' are used to indicate 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, * <blockquote><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></blockquote> * * <p> * Likewise, parsing with a {@code MessageFormat} object using patterns containing * multiple occurrences of the same argument would return the last match. For * example, * <blockquote><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></blockquote> * * <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 DecimalFormatSymbols * @see ChoiceFormat * @see DateFormat * @see SimpleDateFormat * * @author Mark Davis */ public class MessageFormat extends Format { private static final long serialVersionUID = 6479157306784022952L; /** * 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 */ public MessageFormat(String pattern) { this.locale = Locale.getDefault(Locale.Category.FORMAT); applyPattern(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 * @since 1.4 */ public MessageFormat(String pattern, Locale locale) { this.locale = locale; applyPattern(pattern); } /** * Sets the locale to be used when creating or comparing subformats. * This affects subsequent calls * <ul> * <li>to the {@link #applyPattern applyPattern} * and {@link #toPattern toPattern} methods if format elements specify * a format type and therefore have the subformats created in the * <code>applyPattern</code> method, as well as * <li>to the <code>format</code> and * {@link #formatToCharacterIterator formatToCharacterIterator} methods * if format elements do not specify a format type and therefore have * the subformats created in the formatting methods. * </ul> * Subformats that have already been created are not affected. * * @param locale the locale to be used when creating or comparing subformats */ public void setLocale(Locale locale) { this.locale = locale; } /** * Gets the locale that's used when creating or comparing subformats. * * @return the locale used when creating or comparing subformats */ public Locale getLocale() { return locale; } /** * 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 */ public void applyPattern(String pattern) { StringBuilder[] segments = new StringBuilder[4]; // Allocate only segments[SEG_RAW] here. The rest are // allocated on demand. segments[SEG_RAW] = new StringBuilder(); int part = SEG_RAW; int formatNumber = 0; boolean inQuote = false; int braceStack = 0; maxOffset = -1; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (part == SEG_RAW) { if (ch == '\'') { if (i + 1 < pattern.length() && pattern.charAt(i+1) == '\'') { segments[part].append(ch); // handle doubles ++i; } else { inQuote = !inQuote; } } else if (ch == '{' && !inQuote) { part = SEG_INDEX; if (segments[SEG_INDEX] == null) { segments[SEG_INDEX] = new StringBuilder(); } } else { segments[part].append(ch); } } else { if (inQuote) { // just copy quotes in parts segments[part].append(ch); if (ch == '\'') { inQuote = false; } } else { switch (ch) { case ',': if (part < SEG_MODIFIER) { if (segments[++part] == null) { segments[part] = new StringBuilder(); } } else { segments[part].append(ch); } break; case '{': ++braceStack; segments[part].append(ch); break; case '}': if (braceStack == 0) { part = SEG_RAW; makeFormat(i, formatNumber, segments); formatNumber++; // throw away other segments segments[SEG_INDEX] = null; segments[SEG_TYPE] = null; segments[SEG_MODIFIER] = null; } else { --braceStack; segments[part].append(ch); } break; case ' ': // Skip any leading space chars for SEG_TYPE. if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) { segments[part].append(ch); } break; case '\'': inQuote = true; // fall through, so we keep quotes in other parts default: segments[part].append(ch);