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

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 geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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