Source Code Cross Referenced for MessageFormat.java in  » Internationalization-Localization » icu4j » com » ibm » icu » 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 » Internationalization Localization » icu4j » com.ibm.icu.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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