Source Code Cross Referenced for MessageFormat.java in  » 6.0-JDK-Core » text » java » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » text » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        /*
0027         * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
0028         * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
0029         *
0030         *   The original version of this source code and documentation is copyrighted
0031         * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
0032         * materials are provided under terms of a License Agreement between Taligent
0033         * and Sun. This technology is protected by multiple US and International
0034         * patents. This notice and attribution to Taligent may not be removed.
0035         *   Taligent is a registered trademark of Taligent, Inc.
0036         *
0037         */
0038
0039        package java.text;
0040
0041        import java.io.InvalidObjectException;
0042        import java.io.IOException;
0043        import java.io.ObjectInputStream;
0044        import java.text.DecimalFormat;
0045        import java.util.ArrayList;
0046        import java.util.Arrays;
0047        import java.util.Date;
0048        import java.util.List;
0049        import java.util.Locale;
0050
0051        /**
0052         * <code>MessageFormat</code> provides a means to produce concatenated
0053         * messages in a language-neutral way. Use this to construct messages
0054         * displayed for end users.
0055         *
0056         * <p>
0057         * <code>MessageFormat</code> takes a set of objects, formats them, then
0058         * inserts the formatted strings into the pattern at the appropriate places.
0059         *
0060         * <p>
0061         * <strong>Note:</strong>
0062         * <code>MessageFormat</code> differs from the other <code>Format</code>
0063         * classes in that you create a <code>MessageFormat</code> object with one
0064         * of its constructors (not with a <code>getInstance</code> style factory
0065         * method). The factory methods aren't necessary because <code>MessageFormat</code>
0066         * itself doesn't implement locale specific behavior. Any locale specific
0067         * behavior is defined by the pattern that you provide as well as the
0068         * subformats used for inserted arguments.
0069         *
0070         * <h4><a name="patterns">Patterns and Their Interpretation</a></h4>
0071         *
0072         * <code>MessageFormat</code> uses patterns of the following form:
0073         * <blockquote><pre>
0074         * <i>MessageFormatPattern:</i>
0075         *         <i>String</i>
0076         *         <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i>
0077         *
0078         * <i>FormatElement:</i>
0079         *         { <i>ArgumentIndex</i> }
0080         *         { <i>ArgumentIndex</i> , <i>FormatType</i> }
0081         *         { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> }
0082         *
0083         * <i>FormatType: one of </i>
0084         *         number date time choice
0085         *
0086         * <i>FormatStyle:</i>
0087         *         short
0088         *         medium
0089         *         long
0090         *         full
0091         *         integer
0092         *         currency
0093         *         percent
0094         *         <i>SubformatPattern</i>
0095         *
0096         * <i>String:</i>
0097         *         <i>StringPart<sub>opt</sub></i>
0098         *         <i>String</i> <i>StringPart</i>
0099         *
0100         * <i>StringPart:</i>
0101         *         ''
0102         *         ' <i>QuotedString</i> '
0103         *         <i>UnquotedString</i>
0104         *
0105         * <i>SubformatPattern:</i>
0106         *         <i>SubformatPatternPart<sub>opt</sub></i>
0107         *         <i>SubformatPattern</i> <i>SubformatPatternPart</i>
0108         *
0109         * <i>SubFormatPatternPart:</i>
0110         *         ' <i>QuotedPattern</i> '
0111         *         <i>UnquotedPattern</i>
0112         * </pre></blockquote>
0113         *
0114         * <p>
0115         * Within a <i>String</i>, <code>"''"</code> represents a single
0116         * quote. A <i>QuotedString</i> can contain arbitrary characters
0117         * except single quotes; the surrounding single quotes are removed.
0118         * An <i>UnquotedString</i> can contain arbitrary characters
0119         * except single quotes and left curly brackets. Thus, a string that
0120         * should result in the formatted message "'{0}'" can be written as
0121         * <code>"'''{'0}''"</code> or <code>"'''{0}'''"</code>.
0122         * <p>
0123         * Within a <i>SubformatPattern</i>, different rules apply.
0124         * A <i>QuotedPattern</i> can contain arbitrary characters
0125         * except single quotes; but the surrounding single quotes are
0126         * <strong>not</strong> removed, so they may be interpreted by the
0127         * subformat. For example, <code>"{1,number,$'#',##}"</code> will
0128         * produce a number format with the pound-sign quoted, with a result
0129         * such as: "$#31,45".
0130         * An <i>UnquotedPattern</i> can contain arbitrary characters
0131         * except single quotes, but curly braces within it must be balanced.
0132         * For example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code>
0133         * are valid subformat patterns, but <code>"ab {0'}' de"</code> and
0134         * <code>"ab } de"</code> are not.
0135         * <p>
0136         * <dl><dt><b>Warning:</b><dd>The rules for using quotes within message
0137         * format patterns unfortunately have shown to be somewhat confusing.
0138         * In particular, it isn't always obvious to localizers whether single
0139         * quotes need to be doubled or not. Make sure to inform localizers about
0140         * the rules, and tell them (for example, by using comments in resource
0141         * bundle source files) which strings will be processed by MessageFormat.
0142         * Note that localizers may need to use single quotes in translated
0143         * strings where the original version doesn't have them.
0144         * </dl>
0145         * <p>
0146         * The <i>ArgumentIndex</i> value is a non-negative integer written
0147         * using the digits '0' through '9', and represents an index into the
0148         * <code>arguments</code> array passed to the <code>format</code> methods
0149         * or the result array returned by the <code>parse</code> methods.
0150         * <p>
0151         * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create
0152         * a <code>Format</code> instance for the format element. The following
0153         * table shows how the values map to Format instances. Combinations not
0154         * shown in the table are illegal. A <i>SubformatPattern</i> must
0155         * be a valid pattern string for the Format subclass used.
0156         * <p>
0157         * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances">
0158         *    <tr>
0159         *       <th id="ft">Format Type
0160         *       <th id="fs">Format Style
0161         *       <th id="sc">Subformat Created
0162         *    <tr>
0163         *       <td headers="ft"><i>(none)</i>
0164         *       <td headers="fs"><i>(none)</i>
0165         *       <td headers="sc"><code>null</code>
0166         *    <tr>
0167         *       <td headers="ft" rowspan=5><code>number</code>
0168         *       <td headers="fs"><i>(none)</i>
0169         *       <td headers="sc"><code>NumberFormat.getInstance(getLocale())</code>
0170         *    <tr>
0171         *       <td headers="fs"><code>integer</code>
0172         *       <td headers="sc"><code>NumberFormat.getIntegerInstance(getLocale())</code>
0173         *    <tr>
0174         *       <td headers="fs"><code>currency</code>
0175         *       <td headers="sc"><code>NumberFormat.getCurrencyInstance(getLocale())</code>
0176         *    <tr>
0177         *       <td headers="fs"><code>percent</code>
0178         *       <td headers="sc"><code>NumberFormat.getPercentInstance(getLocale())</code>
0179         *    <tr>
0180         *       <td headers="fs"><i>SubformatPattern</i>
0181         *       <td headers="sc"><code>new DecimalFormat(subformatPattern, DecimalFormatSymbols.getInstance(getLocale()))</code>
0182         *    <tr>
0183         *       <td headers="ft" rowspan=6><code>date</code>
0184         *       <td headers="fs"><i>(none)</i>
0185         *       <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
0186         *    <tr>
0187         *       <td headers="fs"><code>short</code>
0188         *       <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code>
0189         *    <tr>
0190         *       <td headers="fs"><code>medium</code>
0191         *       <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code>
0192         *    <tr>
0193         *       <td headers="fs"><code>long</code>
0194         *       <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code>
0195         *    <tr>
0196         *       <td headers="fs"><code>full</code>
0197         *       <td headers="sc"><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code>
0198         *    <tr>
0199         *       <td headers="fs"><i>SubformatPattern</i>
0200         *       <td headers="sc"><code>new SimpleDateFormat(subformatPattern, getLocale())</code>
0201         *    <tr>
0202         *       <td headers="ft" rowspan=6><code>time</code>
0203         *       <td headers="fs"><i>(none)</i>
0204         *       <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
0205         *    <tr>
0206         *       <td headers="fs"><code>short</code>
0207         *       <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code>
0208         *    <tr>
0209         *       <td headers="fs"><code>medium</code>
0210         *       <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code>
0211         *    <tr>
0212         *       <td headers="fs"><code>long</code>
0213         *       <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code>
0214         *    <tr>
0215         *       <td headers="fs"><code>full</code>
0216         *       <td headers="sc"><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code>
0217         *    <tr>
0218         *       <td headers="fs"><i>SubformatPattern</i>
0219         *       <td headers="sc"><code>new SimpleDateFormat(subformatPattern, getLocale())</code>
0220         *    <tr>
0221         *       <td headers="ft"><code>choice</code>
0222         *       <td headers="fs"><i>SubformatPattern</i>
0223         *       <td headers="sc"><code>new ChoiceFormat(subformatPattern)</code>
0224         * </table>
0225         * <p>
0226         *
0227         * <h4>Usage Information</h4>
0228         *
0229         * <p>
0230         * Here are some examples of usage.
0231         * In real internationalized programs, the message format pattern and other
0232         * static strings will, of course, be obtained from resource bundles.
0233         * Other parameters will be dynamically determined at runtime.
0234         * <p>
0235         * The first example uses the static method <code>MessageFormat.format</code>,
0236         * which internally creates a <code>MessageFormat</code> for one-time use:
0237         * <blockquote><pre>
0238         * int planet = 7;
0239         * String event = "a disturbance in the Force";
0240         *
0241         * String result = MessageFormat.format(
0242         *     "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
0243         *     planet, new Date(), event);
0244         * </pre></blockquote>
0245         * The output is:
0246         * <blockquote><pre>
0247         * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
0248         * </pre></blockquote>
0249         *
0250         * <p>
0251         * The following example creates a <code>MessageFormat</code> instance that
0252         * can be used repeatedly:
0253         * <blockquote><pre>
0254         * int fileCount = 1273;
0255         * String diskName = "MyDisk";
0256         * Object[] testArgs = {new Long(fileCount), diskName};
0257         *
0258         * MessageFormat form = new MessageFormat(
0259         *     "The disk \"{1}\" contains {0} file(s).");
0260         *
0261         * System.out.println(form.format(testArgs));
0262         * </pre></blockquote>
0263         * The output with different values for <code>fileCount</code>:
0264         * <blockquote><pre>
0265         * The disk "MyDisk" contains 0 file(s).
0266         * The disk "MyDisk" contains 1 file(s).
0267         * The disk "MyDisk" contains 1,273 file(s).
0268         * </pre></blockquote>
0269         *
0270         * <p>
0271         * For more sophisticated patterns, you can use a <code>ChoiceFormat</code>
0272         * to produce correct forms for singular and plural:
0273         * <blockquote><pre>
0274         * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
0275         * double[] filelimits = {0,1,2};
0276         * String[] filepart = {"no files","one file","{0,number} files"};
0277         * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
0278         * form.setFormatByArgumentIndex(0, fileform);
0279         *
0280         * int fileCount = 1273;
0281         * String diskName = "MyDisk";
0282         * Object[] testArgs = {new Long(fileCount), diskName};
0283         *
0284         * System.out.println(form.format(testArgs));
0285         * </pre></blockquote>
0286         * The output with different values for <code>fileCount</code>:
0287         * <blockquote><pre>
0288         * The disk "MyDisk" contains no files.
0289         * The disk "MyDisk" contains one file.
0290         * The disk "MyDisk" contains 1,273 files.
0291         * </pre></blockquote>
0292         *
0293         * <p>
0294         * You can create the <code>ChoiceFormat</code> programmatically, as in the
0295         * above example, or by using a pattern. See {@link ChoiceFormat}
0296         * for more information.
0297         * <blockquote><pre>
0298         * form.applyPattern(
0299         *    "There {0,choice,0#are no files|1#is one file|1&lt;are {0,number,integer} files}.");
0300         * </pre></blockquote>
0301         *
0302         * <p>
0303         * <strong>Note:</strong> As we see above, the string produced
0304         * by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated as special;
0305         * occurrences of '{' are used to indicate subformats, and cause recursion.
0306         * If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code>
0307         * programmatically (instead of using the string patterns), then be careful not to
0308         * produce a format that recurses on itself, which will cause an infinite loop.
0309         * <p>
0310         * When a single argument is parsed more than once in the string, the last match
0311         * will be the final result of the parsing.  For example,
0312         * <blockquote><pre>
0313         * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
0314         * Object[] objs = {new Double(3.1415)};
0315         * String result = mf.format( objs );
0316         * // result now equals "3.14, 3.1"
0317         * objs = null;
0318         * objs = mf.parse(result, new ParsePosition(0));
0319         * // objs now equals {new Double(3.1)}
0320         * </pre></blockquote>
0321         *
0322         * <p>
0323         * Likewise, parsing with a MessageFormat object using patterns containing
0324         * multiple occurrences of the same argument would return the last match.  For
0325         * example,
0326         * <blockquote><pre>
0327         * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
0328         * String forParsing = "x, y, z";
0329         * Object[] objs = mf.parse(forParsing, new ParsePosition(0));
0330         * // result now equals {new String("z")}
0331         * </pre></blockquote>
0332         *
0333         * <h4><a name="synchronization">Synchronization</a></h4>
0334         *
0335         * <p>
0336         * Message formats are not synchronized.
0337         * It is recommended to create separate format instances for each thread.
0338         * If multiple threads access a format concurrently, it must be synchronized
0339         * externally.
0340         *
0341         * @see          java.util.Locale
0342         * @see          Format
0343         * @see          NumberFormat
0344         * @see          DecimalFormat
0345         * @see          ChoiceFormat
0346         * @version      1.69, 05/05/07
0347         * @author       Mark Davis
0348         */
0349
0350        public class MessageFormat extends Format {
0351
0352            private static final long serialVersionUID = 6479157306784022952L;
0353
0354            /**
0355             * Constructs a MessageFormat for the default locale and the
0356             * specified pattern.
0357             * The constructor first sets the locale, then parses the pattern and
0358             * creates a list of subformats for the format elements contained in it.
0359             * Patterns and their interpretation are specified in the
0360             * <a href="#patterns">class description</a>.
0361             *
0362             * @param pattern the pattern for this message format
0363             * @exception IllegalArgumentException if the pattern is invalid
0364             */
0365            public MessageFormat(String pattern) {
0366                this .locale = Locale.getDefault();
0367                applyPattern(pattern);
0368            }
0369
0370            /**
0371             * Constructs a MessageFormat for the specified locale and
0372             * pattern.
0373             * The constructor first sets the locale, then parses the pattern and
0374             * creates a list of subformats for the format elements contained in it.
0375             * Patterns and their interpretation are specified in the
0376             * <a href="#patterns">class description</a>.
0377             *
0378             * @param pattern the pattern for this message format
0379             * @param locale the locale for this message format
0380             * @exception IllegalArgumentException if the pattern is invalid
0381             * @since 1.4
0382             */
0383            public MessageFormat(String pattern, Locale locale) {
0384                this .locale = locale;
0385                applyPattern(pattern);
0386            }
0387
0388            /**
0389             * Sets the locale to be used when creating or comparing subformats.
0390             * This affects subsequent calls
0391             * <ul>
0392             * <li>to the {@link #applyPattern applyPattern}
0393             *     and {@link #toPattern toPattern} methods if format elements specify
0394             *     a format type and therefore have the subformats created in the
0395             *     <code>applyPattern</code> method, as well as
0396             * <li>to the <code>format</code> and
0397             *     {@link #formatToCharacterIterator formatToCharacterIterator} methods
0398             *     if format elements do not specify a format type and therefore have
0399             *     the subformats created in the formatting methods.
0400             * </ul>
0401             * Subformats that have already been created are not affected.
0402             *
0403             * @param locale the locale to be used when creating or comparing subformats
0404             */
0405            public void setLocale(Locale locale) {
0406                this .locale = locale;
0407            }
0408
0409            /**
0410             * Gets the locale that's used when creating or comparing subformats.
0411             *
0412             * @return the locale used when creating or comparing subformats
0413             */
0414            public Locale getLocale() {
0415                return locale;
0416            }
0417
0418            /**
0419             * Sets the pattern used by this message format.
0420             * The method parses the pattern and creates a list of subformats
0421             * for the format elements contained in it.
0422             * Patterns and their interpretation are specified in the
0423             * <a href="#patterns">class description</a>.
0424             * 
0425             * @param pattern the pattern for this message format
0426             * @exception IllegalArgumentException if the pattern is invalid
0427             */
0428            public void applyPattern(String pattern) {
0429                StringBuffer[] segments = new StringBuffer[4];
0430                for (int i = 0; i < segments.length; ++i) {
0431                    segments[i] = new StringBuffer();
0432                }
0433                int part = 0;
0434                int formatNumber = 0;
0435                boolean inQuote = false;
0436                int braceStack = 0;
0437                maxOffset = -1;
0438                for (int i = 0; i < pattern.length(); ++i) {
0439                    char ch = pattern.charAt(i);
0440                    if (part == 0) {
0441                        if (ch == '\'') {
0442                            if (i + 1 < pattern.length()
0443                                    && pattern.charAt(i + 1) == '\'') {
0444                                segments[part].append(ch); // handle doubles
0445                                ++i;
0446                            } else {
0447                                inQuote = !inQuote;
0448                            }
0449                        } else if (ch == '{' && !inQuote) {
0450                            part = 1;
0451                        } else {
0452                            segments[part].append(ch);
0453                        }
0454                    } else if (inQuote) { // just copy quotes in parts
0455                        segments[part].append(ch);
0456                        if (ch == '\'') {
0457                            inQuote = false;
0458                        }
0459                    } else {
0460                        switch (ch) {
0461                        case ',':
0462                            if (part < 3)
0463                                part += 1;
0464                            else
0465                                segments[part].append(ch);
0466                            break;
0467                        case '{':
0468                            ++braceStack;
0469                            segments[part].append(ch);
0470                            break;
0471                        case '}':
0472                            if (braceStack == 0) {
0473                                part = 0;
0474                                makeFormat(i, formatNumber, segments);
0475                                formatNumber++;
0476                            } else {
0477                                --braceStack;
0478                                segments[part].append(ch);
0479                            }
0480                            break;
0481                        case '\'':
0482                            inQuote = true;
0483                            // fall through, so we keep quotes in other parts
0484                        default:
0485                            segments[part].append(ch);
0486                            break;
0487                        }
0488                    }
0489                }
0490                if (braceStack == 0 && part != 0) {
0491                    maxOffset = -1;
0492                    throw new IllegalArgumentException(
0493                            "Unmatched braces in the pattern.");
0494                }
0495                this .pattern = segments[0].toString();
0496            }
0497
0498            /**
0499             * Returns a pattern representing the current state of the message format.
0500             * The string is constructed from internal information and therefore
0501             * does not necessarily equal the previously applied pattern. 
0502             *
0503             * @return a pattern representing the current state of the message format
0504             */
0505            public String toPattern() {
0506                // later, make this more extensible
0507                int lastOffset = 0;
0508                StringBuffer result = new StringBuffer();
0509                for (int i = 0; i <= maxOffset; ++i) {
0510                    copyAndFixQuotes(pattern, lastOffset, offsets[i], result);
0511                    lastOffset = offsets[i];
0512                    result.append('{');
0513                    result.append(argumentNumbers[i]);
0514                    if (formats[i] == null) {
0515                        // do nothing, string format
0516                    } else if (formats[i] instanceof  DecimalFormat) {
0517                        if (formats[i].equals(NumberFormat.getInstance(locale))) {
0518                            result.append(",number");
0519                        } else if (formats[i].equals(NumberFormat
0520                                .getCurrencyInstance(locale))) {
0521                            result.append(",number,currency");
0522                        } else if (formats[i].equals(NumberFormat
0523                                .getPercentInstance(locale))) {
0524                            result.append(",number,percent");
0525                        } else if (formats[i].equals(NumberFormat
0526                                .getIntegerInstance(locale))) {
0527                            result.append(",number,integer");
0528                        } else {
0529                            result.append(",number,"
0530                                    + ((DecimalFormat) formats[i]).toPattern());
0531                        }
0532                    } else if (formats[i] instanceof  SimpleDateFormat) {
0533                        if (formats[i].equals(DateFormat.getDateInstance(
0534                                DateFormat.DEFAULT, locale))) {
0535                            result.append(",date");
0536                        } else if (formats[i].equals(DateFormat
0537                                .getDateInstance(DateFormat.SHORT, locale))) {
0538                            result.append(",date,short");
0539                        } else if (formats[i].equals(DateFormat
0540                                .getDateInstance(DateFormat.DEFAULT, locale))) {
0541                            result.append(",date,medium");
0542                        } else if (formats[i].equals(DateFormat
0543                                .getDateInstance(DateFormat.LONG, locale))) {
0544                            result.append(",date,long");
0545                        } else if (formats[i].equals(DateFormat
0546                                .getDateInstance(DateFormat.FULL, locale))) {
0547                            result.append(",date,full");
0548                        } else if (formats[i].equals(DateFormat
0549                                .getTimeInstance(DateFormat.DEFAULT, locale))) {
0550                            result.append(",time");
0551                        } else if (formats[i].equals(DateFormat
0552                                .getTimeInstance(DateFormat.SHORT, locale))) {
0553                            result.append(",time,short");
0554                        } else if (formats[i].equals(DateFormat
0555                                .getTimeInstance(DateFormat.DEFAULT, locale))) {
0556                            result.append(",time,medium");
0557                        } else if (formats[i].equals(DateFormat
0558                                .getTimeInstance(DateFormat.LONG, locale))) {
0559                            result.append(",time,long");
0560                        } else if (formats[i].equals(DateFormat
0561                                .getTimeInstance(DateFormat.FULL, locale))) {
0562                            result.append(",time,full");
0563                        } else {
0564                            result.append(",date,"
0565                                    + ((SimpleDateFormat) formats[i])
0566                                            .toPattern());
0567                        }
0568                    } else if (formats[i] instanceof  ChoiceFormat) {
0569                        result.append(",choice,"
0570                                + ((ChoiceFormat) formats[i]).toPattern());
0571                    } else {
0572                        //result.append(", unknown");
0573                    }
0574                    result.append('}');
0575                }
0576                copyAndFixQuotes(pattern, lastOffset, pattern.length(), result);
0577                return result.toString();
0578            }
0579
0580            /**
0581             * Sets the formats to use for the values passed into
0582             * <code>format</code> methods or returned from <code>parse</code>
0583             * methods. The indices of elements in <code>newFormats</code>
0584             * correspond to the argument indices used in the previously set
0585             * pattern string.
0586             * The order of formats in <code>newFormats</code> thus corresponds to
0587             * the order of elements in the <code>arguments</code> array passed
0588             * to the <code>format</code> methods or the result array returned
0589             * by the <code>parse</code> methods.
0590             * <p>
0591             * If an argument index is used for more than one format element
0592             * in the pattern string, then the corresponding new format is used
0593             * for all such format elements. If an argument index is not used
0594             * for any format element in the pattern string, then the
0595             * corresponding new format is ignored. If fewer formats are provided
0596             * than needed, then only the formats for argument indices less
0597             * than <code>newFormats.length</code> are replaced.
0598             *
0599             * @param newFormats the new formats to use
0600             * @exception NullPointerException if <code>newFormats</code> is null
0601             * @since 1.4
0602             */
0603            public void setFormatsByArgumentIndex(Format[] newFormats) {
0604                for (int i = 0; i <= maxOffset; i++) {
0605                    int j = argumentNumbers[i];
0606                    if (j < newFormats.length) {
0607                        formats[i] = newFormats[j];
0608                    }
0609                }
0610            }
0611
0612            /**
0613             * Sets the formats to use for the format elements in the
0614             * previously set pattern string.
0615             * The order of formats in <code>newFormats</code> corresponds to
0616             * the order of format elements in the pattern string.
0617             * <p>
0618             * If more formats are provided than needed by the pattern string,
0619             * the remaining ones are ignored. If fewer formats are provided
0620             * than needed, then only the first <code>newFormats.length</code>
0621             * formats are replaced.
0622             * <p>
0623             * Since the order of format elements in a pattern string often
0624             * changes during localization, it is generally better to use the
0625             * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}
0626             * method, which assumes an order of formats corresponding to the
0627             * order of elements in the <code>arguments</code> array passed to
0628             * the <code>format</code> methods or the result array returned by
0629             * the <code>parse</code> methods.
0630             *
0631             * @param newFormats the new formats to use
0632             * @exception NullPointerException if <code>newFormats</code> is null
0633             */
0634            public void setFormats(Format[] newFormats) {
0635                int runsToCopy = newFormats.length;
0636                if (runsToCopy > maxOffset + 1) {
0637                    runsToCopy = maxOffset + 1;
0638                }
0639                for (int i = 0; i < runsToCopy; i++) {
0640                    formats[i] = newFormats[i];
0641                }
0642            }
0643
0644            /**
0645             * Sets the format to use for the format elements within the
0646             * previously set pattern string that use the given argument
0647             * index.
0648             * The argument index is part of the format element definition and
0649             * represents an index into the <code>arguments</code> array passed
0650             * to the <code>format</code> methods or the result array returned
0651             * by the <code>parse</code> methods.
0652             * <p>
0653             * If the argument index is used for more than one format element
0654             * in the pattern string, then the new format is used for all such
0655             * format elements. If the argument index is not used for any format
0656             * element in the pattern string, then the new format is ignored.
0657             *
0658             * @param argumentIndex the argument index for which to use the new format
0659             * @param newFormat the new format to use
0660             * @since 1.4
0661             */
0662            public void setFormatByArgumentIndex(int argumentIndex,
0663                    Format newFormat) {
0664                for (int j = 0; j <= maxOffset; j++) {
0665                    if (argumentNumbers[j] == argumentIndex) {
0666                        formats[j] = newFormat;
0667                    }
0668                }
0669            }
0670
0671            /**
0672             * Sets the format to use for the format element with the given
0673             * format element index within the previously set pattern string.
0674             * The format element index is the zero-based number of the format
0675             * element counting from the start of the pattern string.
0676             * <p>
0677             * Since the order of format elements in a pattern string often
0678             * changes during localization, it is generally better to use the
0679             * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}
0680             * method, which accesses format elements based on the argument
0681             * index they specify.
0682             *
0683             * @param formatElementIndex the index of a format element within the pattern
0684             * @param newFormat the format to use for the specified format element
0685             * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or
0686             *            larger than the number of format elements in the pattern string
0687             */
0688            public void setFormat(int formatElementIndex, Format newFormat) {
0689                formats[formatElementIndex] = newFormat;
0690            }
0691
0692            /**
0693             * Gets the formats used for the values passed into
0694             * <code>format</code> methods or returned from <code>parse</code>
0695             * methods. The indices of elements in the returned array
0696             * correspond to the argument indices used in the previously set
0697             * pattern string.
0698             * The order of formats in the returned array thus corresponds to
0699             * the order of elements in the <code>arguments</code> array passed
0700             * to the <code>format</code> methods or the result array returned
0701             * by the <code>parse</code> methods.
0702             * <p>
0703             * If an argument index is used for more than one format element
0704             * in the pattern string, then the format used for the last such
0705             * format element is returned in the array. If an argument index
0706             * is not used for any format element in the pattern string, then
0707             * null is returned in the array.
0708             *
0709             * @return the formats used for the arguments within the pattern
0710             * @since 1.4
0711             */
0712            public Format[] getFormatsByArgumentIndex() {
0713                int maximumArgumentNumber = -1;
0714                for (int i = 0; i <= maxOffset; i++) {
0715                    if (argumentNumbers[i] > maximumArgumentNumber) {
0716                        maximumArgumentNumber = argumentNumbers[i];
0717                    }
0718                }
0719                Format[] resultArray = new Format[maximumArgumentNumber + 1];
0720                for (int i = 0; i <= maxOffset; i++) {
0721                    resultArray[argumentNumbers[i]] = formats[i];
0722                }
0723                return resultArray;
0724            }
0725
0726            /**
0727             * Gets the formats used for the format elements in the
0728             * previously set pattern string.
0729             * The order of formats in the returned array corresponds to
0730             * the order of format elements in the pattern string.
0731             * <p>
0732             * Since the order of format elements in a pattern string often
0733             * changes during localization, it's generally better to use the
0734             * {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex}
0735             * method, which assumes an order of formats corresponding to the
0736             * order of elements in the <code>arguments</code> array passed to
0737             * the <code>format</code> methods or the result array returned by
0738             * the <code>parse</code> methods.
0739             *
0740             * @return the formats used for the format elements in the pattern
0741             */
0742            public Format[] getFormats() {
0743                Format[] resultArray = new Format[maxOffset + 1];
0744                System.arraycopy(formats, 0, resultArray, 0, maxOffset + 1);
0745                return resultArray;
0746            }
0747
0748            /**
0749             * Formats an array of objects and appends the <code>MessageFormat</code>'s
0750             * pattern, with format elements replaced by the formatted objects, to the
0751             * provided <code>StringBuffer</code>.
0752             * <p>
0753             * The text substituted for the individual format elements is derived from
0754             * the current subformat of the format element and the
0755             * <code>arguments</code> element at the format element's argument index
0756             * as indicated by the first matching line of the following table. An
0757             * argument is <i>unavailable</i> if <code>arguments</code> is
0758             * <code>null</code> or has fewer than argumentIndex+1 elements.
0759             * <p>
0760             * <table border=1 summary="Examples of subformat,argument,and formatted text">
0761             *    <tr>
0762             *       <th>Subformat
0763             *       <th>Argument
0764             *       <th>Formatted Text
0765             *    <tr>
0766             *       <td><i>any</i>
0767             *       <td><i>unavailable</i>
0768             *       <td><code>"{" + argumentIndex + "}"</code>
0769             *    <tr>
0770             *       <td><i>any</i>
0771             *       <td><code>null</code>
0772             *       <td><code>"null"</code>
0773             *    <tr>
0774             *       <td><code>instanceof ChoiceFormat</code>
0775             *       <td><i>any</i>
0776             *       <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>
0777             *           (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :
0778             *           subformat.format(argument)</code>
0779             *    <tr>
0780             *       <td><code>!= null</code>
0781             *       <td><i>any</i>
0782             *       <td><code>subformat.format(argument)</code>
0783             *    <tr>
0784             *       <td><code>null</code>
0785             *       <td><code>instanceof Number</code>
0786             *       <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
0787             *    <tr>
0788             *       <td><code>null</code>
0789             *       <td><code>instanceof Date</code>
0790             *       <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>
0791             *    <tr>
0792             *       <td><code>null</code>
0793             *       <td><code>instanceof String</code>
0794             *       <td><code>argument</code>
0795             *    <tr>
0796             *       <td><code>null</code>
0797             *       <td><i>any</i>
0798             *       <td><code>argument.toString()</code>
0799             * </table>
0800             * <p>
0801             * If <code>pos</code> is non-null, and refers to
0802             * <code>Field.ARGUMENT</code>, the location of the first formatted
0803             * string will be returned.
0804             *
0805             * @param arguments an array of objects to be formatted and substituted.
0806             * @param result where text is appended.
0807             * @param pos On input: an alignment field, if desired.
0808             *            On output: the offsets of the alignment field.
0809             * @exception IllegalArgumentException if an argument in the
0810             *            <code>arguments</code> array is not of the type
0811             *            expected by the format element(s) that use it.
0812             */
0813            public final StringBuffer format(Object[] arguments,
0814                    StringBuffer result, FieldPosition pos) {
0815                return subformat(arguments, result, pos, null);
0816            }
0817
0818            /**
0819             * Creates a MessageFormat with the given pattern and uses it
0820             * to format the given arguments. This is equivalent to
0821             * <blockquote>
0822             *     <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>
0823             * </blockquote>
0824             *
0825             * @exception IllegalArgumentException if the pattern is invalid,
0826             *            or if an argument in the <code>arguments</code> array
0827             *            is not of the type expected by the format element(s)
0828             *            that use it.
0829             */
0830            public static String format(String pattern, Object... arguments) {
0831                MessageFormat temp = new MessageFormat(pattern);
0832                return temp.format(arguments);
0833            }
0834
0835            // Overrides
0836            /**
0837             * Formats an array of objects and appends the <code>MessageFormat</code>'s
0838             * pattern, with format elements replaced by the formatted objects, to the
0839             * provided <code>StringBuffer</code>.
0840             * This is equivalent to
0841             * <blockquote>
0842             *     <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
0843             * </blockquote>
0844             *
0845             * @param arguments an array of objects to be formatted and substituted.
0846             * @param result where text is appended.
0847             * @param pos On input: an alignment field, if desired.
0848             *            On output: the offsets of the alignment field.
0849             * @exception IllegalArgumentException if an argument in the
0850             *            <code>arguments</code> array is not of the type
0851             *            expected by the format element(s) that use it.
0852             */
0853            public final StringBuffer format(Object arguments,
0854                    StringBuffer result, FieldPosition pos) {
0855                return subformat((Object[]) arguments, result, pos, null);
0856            }
0857
0858            /**
0859             * Formats an array of objects and inserts them into the
0860             * <code>MessageFormat</code>'s pattern, producing an
0861             * <code>AttributedCharacterIterator</code>.
0862             * You can use the returned <code>AttributedCharacterIterator</code>
0863             * to build the resulting String, as well as to determine information
0864             * about the resulting String.
0865             * <p>
0866             * The text of the returned <code>AttributedCharacterIterator</code> is
0867             * the same that would be returned by
0868             * <blockquote>
0869             *     <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
0870             * </blockquote>
0871             * <p>
0872             * In addition, the <code>AttributedCharacterIterator</code> contains at
0873             * least attributes indicating where text was generated from an
0874             * argument in the <code>arguments</code> array. The keys of these attributes are of
0875             * type <code>MessageFormat.Field</code>, their values are
0876             * <code>Integer</code> objects indicating the index in the <code>arguments</code>
0877             * array of the argument from which the text was generated.
0878             * <p>
0879             * The attributes/value from the underlying <code>Format</code>
0880             * instances that <code>MessageFormat</code> uses will also be
0881             * placed in the resulting <code>AttributedCharacterIterator</code>.
0882             * This allows you to not only find where an argument is placed in the
0883             * resulting String, but also which fields it contains in turn.
0884             *
0885             * @param arguments an array of objects to be formatted and substituted.
0886             * @return AttributedCharacterIterator describing the formatted value.
0887             * @exception NullPointerException if <code>arguments</code> is null.
0888             * @exception IllegalArgumentException if an argument in the
0889             *            <code>arguments</code> array is not of the type
0890             *            expected by the format element(s) that use it.
0891             * @since 1.4
0892             */
0893            public AttributedCharacterIterator formatToCharacterIterator(
0894                    Object arguments) {
0895                StringBuffer result = new StringBuffer();
0896                ArrayList iterators = new ArrayList();
0897
0898                if (arguments == null) {
0899                    throw new NullPointerException(
0900                            "formatToCharacterIterator must be passed non-null object");
0901                }
0902                subformat((Object[]) arguments, result, null, iterators);
0903                if (iterators.size() == 0) {
0904                    return createAttributedCharacterIterator("");
0905                }
0906                return createAttributedCharacterIterator((AttributedCharacterIterator[]) iterators
0907                        .toArray(new AttributedCharacterIterator[iterators
0908                                .size()]));
0909            }
0910
0911            /**
0912             * Parses the string.
0913             *
0914             * <p>Caveats: The parse may fail in a number of circumstances.
0915             * For example:
0916             * <ul>
0917             * <li>If one of the arguments does not occur in the pattern.
0918             * <li>If the format of an argument loses information, such as
0919             *     with a choice format where a large number formats to "many".
0920             * <li>Does not yet handle recursion (where
0921             *     the substituted strings contain {n} references.)
0922             * <li>Will not always find a match (or the correct match)
0923             *     if some part of the parse is ambiguous.
0924             *     For example, if the pattern "{1},{2}" is used with the
0925             *     string arguments {"a,b", "c"}, it will format as "a,b,c".
0926             *     When the result is parsed, it will return {"a", "b,c"}.
0927             * <li>If a single argument is parsed more than once in the string,
0928             *     then the later parse wins.
0929             * </ul>
0930             * When the parse fails, use ParsePosition.getErrorIndex() to find out
0931             * where in the string the parsing failed.  The returned error
0932             * index is the starting offset of the sub-patterns that the string
0933             * is comparing with.  For example, if the parsing string "AAA {0} BBB"
0934             * is comparing against the pattern "AAD {0} BBB", the error index is
0935             * 0. When an error occurs, the call to this method will return null.
0936             * If the source is null, return an empty array.
0937             */
0938            public Object[] parse(String source, ParsePosition pos) {
0939                if (source == null) {
0940                    Object[] empty = {};
0941                    return empty;
0942                }
0943
0944                int maximumArgumentNumber = -1;
0945                for (int i = 0; i <= maxOffset; i++) {
0946                    if (argumentNumbers[i] > maximumArgumentNumber) {
0947                        maximumArgumentNumber = argumentNumbers[i];
0948                    }
0949                }
0950                Object[] resultArray = new Object[maximumArgumentNumber + 1];
0951
0952                int patternOffset = 0;
0953                int sourceOffset = pos.index;
0954                ParsePosition tempStatus = new ParsePosition(0);
0955                for (int i = 0; i <= maxOffset; ++i) {
0956                    // match up to format
0957                    int len = offsets[i] - patternOffset;
0958                    if (len == 0
0959                            || pattern.regionMatches(patternOffset, source,
0960                                    sourceOffset, len)) {
0961                        sourceOffset += len;
0962                        patternOffset += len;
0963                    } else {
0964                        pos.errorIndex = sourceOffset;
0965                        return null; // leave index as is to signal error
0966                    }
0967
0968                    // now use format
0969                    if (formats[i] == null) { // string format
0970                        // if at end, use longest possible match
0971                        // otherwise uses first match to intervening string
0972                        // does NOT recursively try all possibilities
0973                        int tempLength = (i != maxOffset) ? offsets[i + 1]
0974                                : pattern.length();
0975
0976                        int next;
0977                        if (patternOffset >= tempLength) {
0978                            next = source.length();
0979                        } else {
0980                            next = source.indexOf(pattern.substring(
0981                                    patternOffset, tempLength), sourceOffset);
0982                        }
0983
0984                        if (next < 0) {
0985                            pos.errorIndex = sourceOffset;
0986                            return null; // leave index as is to signal error
0987                        } else {
0988                            String strValue = source.substring(sourceOffset,
0989                                    next);
0990                            if (!strValue
0991                                    .equals("{" + argumentNumbers[i] + "}"))
0992                                resultArray[argumentNumbers[i]] = source
0993                                        .substring(sourceOffset, next);
0994                            sourceOffset = next;
0995                        }
0996                    } else {
0997                        tempStatus.index = sourceOffset;
0998                        resultArray[argumentNumbers[i]] = formats[i]
0999                                .parseObject(source, tempStatus);
1000                        if (tempStatus.index == sourceOffset) {
1001                            pos.errorIndex = sourceOffset;
1002                            return null; // leave index as is to signal error
1003                        }
1004                        sourceOffset = tempStatus.index; // update
1005                    }
1006                }
1007                int len = pattern.length() - patternOffset;
1008                if (len == 0
1009                        || pattern.regionMatches(patternOffset, source,
1010                                sourceOffset, len)) {
1011                    pos.index = sourceOffset + len;
1012                } else {
1013                    pos.errorIndex = sourceOffset;
1014                    return null; // leave index as is to signal error
1015                }
1016                return resultArray;
1017            }
1018
1019            /**
1020             * Parses text from the beginning of the given string to produce an object
1021             * array.
1022             * The method may not use the entire text of the given string.
1023             * <p>
1024             * See the {@link #parse(String, ParsePosition)} method for more information
1025             * on message parsing.
1026             *
1027             * @param source A <code>String</code> whose beginning should be parsed.
1028             * @return An <code>Object</code> array parsed from the string.
1029             * @exception ParseException if the beginning of the specified string
1030             *            cannot be parsed.
1031             */
1032            public Object[] parse(String source) throws ParseException {
1033                ParsePosition pos = new ParsePosition(0);
1034                Object[] result = parse(source, pos);
1035                if (pos.index == 0) // unchanged, returned object is null
1036                    throw new ParseException("MessageFormat parse error!",
1037                            pos.errorIndex);
1038
1039                return result;
1040            }
1041
1042            /**
1043             * Parses text from a string to produce an object array.
1044             * <p>
1045             * The method attempts to parse text starting at the index given by
1046             * <code>pos</code>.
1047             * If parsing succeeds, then the index of <code>pos</code> is updated
1048             * to the index after the last character used (parsing does not necessarily
1049             * use all characters up to the end of the string), and the parsed
1050             * object array is returned. The updated <code>pos</code> can be used to
1051             * indicate the starting point for the next call to this method.
1052             * If an error occurs, then the index of <code>pos</code> is not
1053             * changed, the error index of <code>pos</code> is set to the index of
1054             * the character where the error occurred, and null is returned.
1055             * <p>
1056             * See the {@link #parse(String, ParsePosition)} method for more information
1057             * on message parsing.
1058             *
1059             * @param source A <code>String</code>, part of which should be parsed.
1060             * @param pos A <code>ParsePosition</code> object with index and error
1061             *            index information as described above.
1062             * @return An <code>Object</code> array parsed from the string. In case of
1063             *         error, returns null.
1064             * @exception NullPointerException if <code>pos</code> is null.
1065             */
1066            public Object parseObject(String source, ParsePosition pos) {
1067                return parse(source, pos);
1068            }
1069
1070            /**
1071             * Creates and returns a copy of this object.
1072             *
1073             * @return a clone of this instance.
1074             */
1075            public Object clone() {
1076                MessageFormat other = (MessageFormat) super .clone();
1077
1078                // clone arrays. Can't do with utility because of bug in Cloneable
1079                other.formats = (Format[]) formats.clone(); // shallow clone
1080                for (int i = 0; i < formats.length; ++i) {
1081                    if (formats[i] != null)
1082                        other.formats[i] = (Format) formats[i].clone();
1083                }
1084                // for primitives or immutables, shallow clone is enough
1085                other.offsets = (int[]) offsets.clone();
1086                other.argumentNumbers = (int[]) argumentNumbers.clone();
1087
1088                return other;
1089            }
1090
1091            /**
1092             * Equality comparison between two message format objects
1093             */
1094            public boolean equals(Object obj) {
1095                if (this  == obj) // quick check
1096                    return true;
1097                if (obj == null || getClass() != obj.getClass())
1098                    return false;
1099                MessageFormat other = (MessageFormat) obj;
1100                return (maxOffset == other.maxOffset
1101                        && pattern.equals(other.pattern)
1102                        && ((locale != null && locale.equals(other.locale)) || (locale == null && other.locale == null))
1103                        && Arrays.equals(offsets, other.offsets)
1104                        && Arrays
1105                                .equals(argumentNumbers, other.argumentNumbers) && Arrays
1106                        .equals(formats, other.formats));
1107            }
1108
1109            /**
1110             * Generates a hash code for the message format object.
1111             */
1112            public int hashCode() {
1113                return pattern.hashCode(); // enough for reasonable distribution
1114            }
1115
1116            /**
1117             * Defines constants that are used as attribute keys in the
1118             * <code>AttributedCharacterIterator</code> returned
1119             * from <code>MessageFormat.formatToCharacterIterator</code>.
1120             *
1121             * @since 1.4
1122             */
1123            public static class Field extends Format.Field {
1124
1125                // Proclaim serial compatibility with 1.4 FCS
1126                private static final long serialVersionUID = 7899943957617360810L;
1127
1128                /**
1129                 * Creates a Field with the specified name.
1130                 *
1131                 * @param name Name of the attribute
1132                 */
1133                protected Field(String name) {
1134                    super (name);
1135                }
1136
1137                /**
1138                 * Resolves instances being deserialized to the predefined constants.
1139                 *
1140                 * @throws InvalidObjectException if the constant could not be
1141                 *         resolved.
1142                 * @return resolved MessageFormat.Field constant
1143                 */
1144                protected Object readResolve() throws InvalidObjectException {
1145                    if (this .getClass() != MessageFormat.Field.class) {
1146                        throw new InvalidObjectException(
1147                                "subclass didn't correctly implement readResolve");
1148                    }
1149
1150                    return ARGUMENT;
1151                }
1152
1153                //
1154                // The constants
1155                //
1156
1157                /**
1158                 * Constant identifying a portion of a message that was generated
1159                 * from an argument passed into <code>formatToCharacterIterator</code>.
1160                 * The value associated with the key will be an <code>Integer</code>
1161                 * indicating the index in the <code>arguments</code> array of the
1162                 * argument from which the text was generated.
1163                 */
1164                public final static Field ARGUMENT = new Field(
1165                        "message argument field");
1166            }
1167
1168            // ===========================privates============================
1169
1170            /**
1171             * The locale to use for formatting numbers and dates.
1172             * @serial
1173             */
1174            private Locale locale;
1175
1176            /**
1177             * The string that the formatted values are to be plugged into.  In other words, this
1178             * is the pattern supplied on construction with all of the {} expressions taken out.
1179             * @serial
1180             */
1181            private String pattern = "";
1182
1183            /** The initially expected number of subformats in the format */
1184            private static final int INITIAL_FORMATS = 10;
1185
1186            /**
1187             * An array of formatters, which are used to format the arguments.
1188             * @serial
1189             */
1190            private Format[] formats = new Format[INITIAL_FORMATS];
1191
1192            /**
1193             * The positions where the results of formatting each argument are to be inserted
1194             * into the pattern.
1195             * @serial
1196             */
1197            private int[] offsets = new int[INITIAL_FORMATS];
1198
1199            /**
1200             * The argument numbers corresponding to each formatter.  (The formatters are stored
1201             * in the order they occur in the pattern, not in the order in which the arguments
1202             * are specified.)
1203             * @serial
1204             */
1205            private int[] argumentNumbers = new int[INITIAL_FORMATS];
1206
1207            /**
1208             * One less than the number of entries in <code>offsets</code>.  Can also be thought of
1209             * as the index of the highest-numbered element in <code>offsets</code> that is being used.
1210             * All of these arrays should have the same number of elements being used as <code>offsets</code>
1211             * does, and so this variable suffices to tell us how many entries are in all of them.
1212             * @serial
1213             */
1214            private int maxOffset = -1;
1215
1216            /**
1217             * Internal routine used by format. If <code>characterIterators</code> is
1218             * non-null, AttributedCharacterIterator will be created from the
1219             * subformats as necessary. If <code>characterIterators</code> is null
1220             * and <code>fp</code> is non-null and identifies
1221             * <code>Field.MESSAGE_ARGUMENT</code>, the location of
1222             * the first replaced argument will be set in it.
1223             *
1224             * @exception IllegalArgumentException if an argument in the
1225             *            <code>arguments</code> array is not of the type
1226             *            expected by the format element(s) that use it.
1227             */
1228            private StringBuffer subformat(Object[] arguments,
1229                    StringBuffer result, FieldPosition fp,
1230                    List characterIterators) {
1231                // note: this implementation assumes a fast substring & index.
1232                // if this is not true, would be better to append chars one by one.
1233                int lastOffset = 0;
1234                int last = result.length();
1235                for (int i = 0; i <= maxOffset; ++i) {
1236                    result.append(pattern.substring(lastOffset, offsets[i]));
1237                    lastOffset = offsets[i];
1238                    int argumentNumber = argumentNumbers[i];
1239                    if (arguments == null || argumentNumber >= arguments.length) {
1240                        result.append("{" + argumentNumber + "}");
1241                        continue;
1242                    }
1243                    // int argRecursion = ((recursionProtection >> (argumentNumber*2)) & 0x3);
1244                    if (false) { // if (argRecursion == 3){
1245                        // prevent loop!!!
1246                        result.append('\uFFFD');
1247                    } else {
1248                        Object obj = arguments[argumentNumber];
1249                        String arg = null;
1250                        Format subFormatter = null;
1251                        if (obj == null) {
1252                            arg = "null";
1253                        } else if (formats[i] != null) {
1254                            subFormatter = formats[i];
1255                            if (subFormatter instanceof  ChoiceFormat) {
1256                                arg = formats[i].format(obj);
1257                                if (arg.indexOf('{') >= 0) {
1258                                    subFormatter = new MessageFormat(arg,
1259                                            locale);
1260                                    obj = arguments;
1261                                    arg = null;
1262                                }
1263                            }
1264                        } else if (obj instanceof  Number) {
1265                            // format number if can
1266                            subFormatter = NumberFormat.getInstance(locale);
1267                        } else if (obj instanceof  Date) {
1268                            // format a Date if can
1269                            subFormatter = DateFormat.getDateTimeInstance(
1270                                    DateFormat.SHORT, DateFormat.SHORT, locale);//fix
1271                        } else if (obj instanceof  String) {
1272                            arg = (String) obj;
1273
1274                        } else {
1275                            arg = obj.toString();
1276                            if (arg == null)
1277                                arg = "null";
1278                        }
1279
1280                        // At this point we are in two states, either subFormatter
1281                        // is non-null indicating we should format obj using it,
1282                        // or arg is non-null and we should use it as the value.
1283
1284                        if (characterIterators != null) {
1285                            // If characterIterators is non-null, it indicates we need
1286                            // to get the CharacterIterator from the child formatter.
1287                            if (last != result.length()) {
1288                                characterIterators
1289                                        .add(createAttributedCharacterIterator(result
1290                                                .substring(last)));
1291                                last = result.length();
1292                            }
1293                            if (subFormatter != null) {
1294                                AttributedCharacterIterator subIterator = subFormatter
1295                                        .formatToCharacterIterator(obj);
1296
1297                                append(result, subIterator);
1298                                if (last != result.length()) {
1299                                    characterIterators
1300                                            .add(createAttributedCharacterIterator(
1301                                                    subIterator,
1302                                                    Field.ARGUMENT,
1303                                                    new Integer(argumentNumber)));
1304                                    last = result.length();
1305                                }
1306                                arg = null;
1307                            }
1308                            if (arg != null && arg.length() > 0) {
1309                                result.append(arg);
1310                                characterIterators
1311                                        .add(createAttributedCharacterIterator(
1312                                                arg, Field.ARGUMENT,
1313                                                new Integer(argumentNumber)));
1314                                last = result.length();
1315                            }
1316                        } else {
1317                            if (subFormatter != null) {
1318                                arg = subFormatter.format(obj);
1319                            }
1320                            last = result.length();
1321                            result.append(arg);
1322                            if (i == 0
1323                                    && fp != null
1324                                    && Field.ARGUMENT.equals(fp
1325                                            .getFieldAttribute())) {
1326                                fp.setBeginIndex(last);
1327                                fp.setEndIndex(result.length());
1328                            }
1329                            last = result.length();
1330                        }
1331                    }
1332                }
1333                result.append(pattern.substring(lastOffset, pattern.length()));
1334                if (characterIterators != null && last != result.length()) {
1335                    characterIterators
1336                            .add(createAttributedCharacterIterator(result
1337                                    .substring(last)));
1338                }
1339                return result;
1340            }
1341
1342            /**
1343             * Convenience method to append all the characters in
1344             * <code>iterator</code> to the StringBuffer <code>result</code>.
1345             */
1346            private void append(StringBuffer result, CharacterIterator iterator) {
1347                if (iterator.first() != CharacterIterator.DONE) {
1348                    char aChar;
1349
1350                    result.append(iterator.first());
1351                    while ((aChar = iterator.next()) != CharacterIterator.DONE) {
1352                        result.append(aChar);
1353                    }
1354                }
1355            }
1356
1357            private static final String[] typeList = { "", "", "number", "",
1358                    "date", "", "time", "", "choice" };
1359            private static final String[] modifierList = { "", "", "currency",
1360                    "", "percent", "", "integer" };
1361            private static final String[] dateModifierList = { "", "", "short",
1362                    "", "medium", "", "long", "", "full" };
1363
1364            private void makeFormat(int position, int offsetNumber,
1365                    StringBuffer[] segments) {
1366                // get the argument number
1367                int argumentNumber;
1368                try {
1369                    argumentNumber = Integer.parseInt(segments[1].toString()); // always unlocalized!
1370                } catch (NumberFormatException e) {
1371                    throw new IllegalArgumentException(
1372                            "can't parse argument number: " + segments[1]);
1373                }
1374                if (argumentNumber < 0) {
1375                    throw new IllegalArgumentException(
1376                            "negative argument number: " + argumentNumber);
1377                }
1378
1379                // resize format information arrays if necessary
1380                if (offsetNumber >= formats.length) {
1381                    int newLength = formats.length * 2;
1382                    Format[] newFormats = new Format[newLength];
1383                    int[] newOffsets = new int[newLength];
1384                    int[] newArgumentNumbers = new int[newLength];
1385                    System.arraycopy(formats, 0, newFormats, 0, maxOffset + 1);
1386                    System.arraycopy(offsets, 0, newOffsets, 0, maxOffset + 1);
1387                    System.arraycopy(argumentNumbers, 0, newArgumentNumbers, 0,
1388                            maxOffset + 1);
1389                    formats = newFormats;
1390                    offsets = newOffsets;
1391                    argumentNumbers = newArgumentNumbers;
1392                }
1393                int oldMaxOffset = maxOffset;
1394                maxOffset = offsetNumber;
1395                offsets[offsetNumber] = segments[0].length();
1396                argumentNumbers[offsetNumber] = argumentNumber;
1397
1398                // now get the format
1399                Format newFormat = null;
1400                switch (findKeyword(segments[2].toString(), typeList)) {
1401                case 0:
1402                    break;
1403                case 1:
1404                case 2:// number
1405                    switch (findKeyword(segments[3].toString(), modifierList)) {
1406                    case 0: // default;
1407                        newFormat = NumberFormat.getInstance(locale);
1408                        break;
1409                    case 1:
1410                    case 2:// currency
1411                        newFormat = NumberFormat.getCurrencyInstance(locale);
1412                        break;
1413                    case 3:
1414                    case 4:// percent
1415                        newFormat = NumberFormat.getPercentInstance(locale);
1416                        break;
1417                    case 5:
1418                    case 6:// integer
1419                        newFormat = NumberFormat.getIntegerInstance(locale);
1420                        break;
1421                    default: // pattern
1422                        newFormat = new DecimalFormat(segments[3].toString(),
1423                                DecimalFormatSymbols.getInstance(locale));
1424                        break;
1425                    }
1426                    break;
1427                case 3:
1428                case 4: // date
1429                    switch (findKeyword(segments[3].toString(),
1430                            dateModifierList)) {
1431                    case 0: // default
1432                        newFormat = DateFormat.getDateInstance(
1433                                DateFormat.DEFAULT, locale);
1434                        break;
1435                    case 1:
1436                    case 2: // short
1437                        newFormat = DateFormat.getDateInstance(
1438                                DateFormat.SHORT, locale);
1439                        break;
1440                    case 3:
1441                    case 4: // medium
1442                        newFormat = DateFormat.getDateInstance(
1443                                DateFormat.DEFAULT, locale);
1444                        break;
1445                    case 5:
1446                    case 6: // long
1447                        newFormat = DateFormat.getDateInstance(DateFormat.LONG,
1448                                locale);
1449                        break;
1450                    case 7:
1451                    case 8: // full
1452                        newFormat = DateFormat.getDateInstance(DateFormat.FULL,
1453                                locale);
1454                        break;
1455                    default:
1456                        newFormat = new SimpleDateFormat(
1457                                segments[3].toString(), locale);
1458                        break;
1459                    }
1460                    break;
1461                case 5:
1462                case 6:// time
1463                    switch (findKeyword(segments[3].toString(),
1464                            dateModifierList)) {
1465                    case 0: // default
1466                        newFormat = DateFormat.getTimeInstance(
1467                                DateFormat.DEFAULT, locale);
1468                        break;
1469                    case 1:
1470                    case 2: // short
1471                        newFormat = DateFormat.getTimeInstance(
1472                                DateFormat.SHORT, locale);
1473                        break;
1474                    case 3:
1475                    case 4: // medium
1476                        newFormat = DateFormat.getTimeInstance(
1477                                DateFormat.DEFAULT, locale);
1478                        break;
1479                    case 5:
1480                    case 6: // long
1481                        newFormat = DateFormat.getTimeInstance(DateFormat.LONG,
1482                                locale);
1483                        break;
1484                    case 7:
1485                    case 8: // full
1486                        newFormat = DateFormat.getTimeInstance(DateFormat.FULL,
1487                                locale);
1488                        break;
1489                    default:
1490                        newFormat = new SimpleDateFormat(
1491                                segments[3].toString(), locale);
1492                        break;
1493                    }
1494                    break;
1495                case 7:
1496                case 8:// choice
1497                    try {
1498                        newFormat = new ChoiceFormat(segments[3].toString());
1499                    } catch (Exception e) {
1500                        maxOffset = oldMaxOffset;
1501                        throw new IllegalArgumentException(
1502                                "Choice Pattern incorrect");
1503                    }
1504                    break;
1505                default:
1506                    maxOffset = oldMaxOffset;
1507                    throw new IllegalArgumentException("unknown format type: "
1508                            + segments[2].toString());
1509                }
1510                formats[offsetNumber] = newFormat;
1511                segments[1].setLength(0); // throw away other segments
1512                segments[2].setLength(0);
1513                segments[3].setLength(0);
1514            }
1515
1516            private static final int findKeyword(String s, String[] list) {
1517                s = s.trim().toLowerCase();
1518                for (int i = 0; i < list.length; ++i) {
1519                    if (s.equals(list[i]))
1520                        return i;
1521                }
1522                return -1;
1523            }
1524
1525            private static final void copyAndFixQuotes(String source,
1526                    int start, int end, StringBuffer target) {
1527                for (int i = start; i < end; ++i) {
1528                    char ch = source.charAt(i);
1529                    if (ch == '{') {
1530                        target.append("'{'");
1531                    } else if (ch == '}') {
1532                        target.append("'}'");
1533                    } else if (ch == '\'') {
1534                        target.append("''");
1535                    } else {
1536                        target.append(ch);
1537                    }
1538                }
1539            }
1540
1541            /**
1542             * After reading an object from the input stream, do a simple verification
1543             * to maintain class invariants.
1544             * @throws InvalidObjectException if the objects read from the stream is invalid.
1545             */
1546            private void readObject(ObjectInputStream in) throws IOException,
1547                    ClassNotFoundException {
1548                in.defaultReadObject();
1549                boolean isValid = maxOffset >= -1 && formats.length > maxOffset
1550                        && offsets.length > maxOffset
1551                        && argumentNumbers.length > maxOffset;
1552                if (isValid) {
1553                    int lastOffset = pattern.length() + 1;
1554                    for (int i = maxOffset; i >= 0; --i) {
1555                        if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
1556                            isValid = false;
1557                            break;
1558                        } else {
1559                            lastOffset = offsets[i];
1560                        }
1561                    }
1562                }
1563                if (!isValid) {
1564                    throw new InvalidObjectException(
1565                            "Could not reconstruct MessageFormat from corrupt stream.");
1566                }
1567            }
1568        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.