12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others. 22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert********************************************************************** 503f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka* Copyright (c) 2004-2016, International Business Machines 67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Corporation and others. All Rights Reserved. 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert********************************************************************** 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Author: Alan Liu 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Created: April 6, 2004 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Since: ICU 3.0 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert********************************************************************** 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*/ 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text; 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException; 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.InvalidObjectException; 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream; 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.AttributedCharacterIterator; 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.AttributedCharacterIterator.Attribute; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.AttributedString; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.CharacterIterator; 227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ChoiceFormat; 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition; 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.Format; 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParseException; 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition; 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList; 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date; 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap; 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet; 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator; 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List; 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale; 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map; 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set; 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.PatternProps; 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.Utility; 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.MessagePattern.ArgType; 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.MessagePattern.Part; 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.FixedDecimal; 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.PluralType; 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ICUUncheckedIOException; 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale; 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale.Category; 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/** 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icuenhanced java.text.MessageFormat}.{@icu _usage_} 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>MessageFormat prepares strings for display to users, 517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with optional arguments (variables/placeholders). 527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The arguments can occur in any order, which is necessary for translation 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * into languages with different grammars. 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>A MessageFormat is constructed from a <em>pattern</em> string 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with arguments in {curly braces} which will be replaced by formatted values. 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p><code>MessageFormat</code> differs from the other <code>Format</code> 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * classes in that you create a <code>MessageFormat</code> object with one 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * of its constructors (not with a <code>getInstance</code> style factory 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method). Factory methods aren't necessary because <code>MessageFormat</code> 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * itself doesn't implement locale-specific behavior. Any locale-specific 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * behavior is defined by the pattern that you provide and the 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * subformats used for inserted arguments. 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Arguments can be named (using identifiers) or numbered (using small ASCII-digit integers). 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Some of the API methods work only with argument numbers and throw an exception 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * if the pattern has named arguments (see {@link #usesNamedArguments()}). 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>An argument might not specify any format type. In this case, 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a Number value is formatted with a default (for the locale) NumberFormat, 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a Date value is formatted with a default (for the locale) DateFormat, 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and for any other value its toString() value is used. 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>An argument might specify a "simple" type for which the specified 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Format object is created, cached and used. 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>An argument might have a "complex" type with nested MessageFormat sub-patterns. 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * During formatting, one of these sub-messages is selected according to the argument value 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and recursively formatted. 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>After construction, a custom Format object can be set for 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a top-level argument, overriding the default formatting and parsing behavior 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for that argument. 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * However, custom formatting can be achieved more simply by writing 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a typeless argument in the pattern string 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and supplying it with a preformatted string value. 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>When formatting, MessageFormat takes a collection of argument values 907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and writes an output string. 917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The argument values may be passed as an array 927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (when the pattern contains only numbered arguments) 937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * or as a Map (which works for both named and numbered arguments). 947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Each argument is matched with one of the input values by array index or map key 967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and formatted according to its pattern specification 977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (or using a custom Format object if one was set). 987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A numbered pattern argument is matched with a map key that contains that number 997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * as an ASCII-decimal-digit string (without leading zero). 1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 101bee65486a185907111f3be60992433e133ec0e32Scott Russell * <h3><a name="patterns">Patterns and Their Interpretation</a></h3> 1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>MessageFormat</code> uses patterns of the following form: 1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote><pre> 1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * message = messageText (argument messageText)* 1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument = noneArg | simpleArg | complexArg 1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * complexArg = choiceArg | pluralArg | selectArg | selectordinalArg 1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * noneArg = '{' argNameOrNumber '}' 1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * simpleArg = '{' argNameOrNumber ',' argType [',' argStyle] '}' 1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * choiceArg = '{' argNameOrNumber ',' "choice" ',' choiceStyle '}' 1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pluralArg = '{' argNameOrNumber ',' "plural" ',' pluralStyle '}' 1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * selectArg = '{' argNameOrNumber ',' "select" ',' selectStyle '}' 1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * selectordinalArg = '{' argNameOrNumber ',' "selectordinal" ',' pluralStyle '}' 1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * choiceStyle: see {@link ChoiceFormat} 1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pluralStyle: see {@link PluralFormat} 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * selectStyle: see {@link SelectFormat} 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argNameOrNumber = argName | argNumber 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argName = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+ 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argNumber = '0' | ('1'..'9' ('0'..'9')*) 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argType = "number" | "date" | "time" | "spellout" | "ordinal" | "duration" 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argStyle = "short" | "medium" | "long" | "full" | "integer" | "currency" | "percent" | argStyleText 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre></blockquote> 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul> 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>messageText can contain quoted literal strings including syntax characters. 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A quoted literal string begins with an ASCII apostrophe and a syntax character 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (usually a {curly brace}) and continues until the next single apostrophe. 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A double ASCII apostrohpe inside or outside of a quoted string represents 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * one literal apostrophe. 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>Quotable syntax characters are the {curly braces} in all messageText parts, 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * plus the '#' sign in a messageText immediately inside a pluralStyle, 1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the '|' symbol in a messageText immediately inside a choiceStyle. 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>See also {@link MessagePattern.ApostropheMode} 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>In argStyleText, every single ASCII apostrophe begins and ends quoted literal text, 1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and unquoted {curly braces} must occur in matched pairs. 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul> 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Recommendation: Use the real apostrophe (single quote) character \u2019 for 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * human-readable text, and use the ASCII apostrophe (\u0027 ' ) 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * only in program syntax, like quoting in MessageFormat. 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * See the annotations for U+0027 Apostrophe in The Unicode Standard. 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>The <code>choice</code> argument type is deprecated. 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Use <code>plural</code> arguments for proper plural selection, 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and <code>select</code> arguments for simple selection among a fixed set of choices. 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>The <code>argType</code> and <code>argStyle</code> values are used to create 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a <code>Format</code> instance for the format element. The following 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * table shows how the values map to Format instances. Combinations not 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * shown in the table are illegal. Any <code>argStyleText</code> must 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * be a valid pattern string for the Format subclass used. 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 157bee65486a185907111f3be60992433e133ec0e32Scott Russell * <table border=1> 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>argType 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>argStyle 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>resulting Format object 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td colspan=2><i>(none)</i> 1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>null</code> 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td rowspan=5><code>number</code> 1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>(none)</i> 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>NumberFormat.getInstance(getLocale())</code> 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>integer</code> 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>NumberFormat.getIntegerInstance(getLocale())</code> 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>currency</code> 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>NumberFormat.getCurrencyInstance(getLocale())</code> 1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>percent</code> 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>NumberFormat.getPercentInstance(getLocale())</code> 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText</i> 1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>new DecimalFormat(argStyleText, new DecimalFormatSymbols(getLocale()))</code> 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td rowspan=6><code>date</code> 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>(none)</i> 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> 1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>short</code> 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code> 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>medium</code> 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>long</code> 1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code> 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>full</code> 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code> 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText</i> 199bee65486a185907111f3be60992433e133ec0e32Scott Russell * <td><code>new SimpleDateFormat(argStyleText, getLocale())</code> 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td rowspan=6><code>time</code> 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>(none)</i> 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>short</code> 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code> 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>medium</code> 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>long</code> 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code> 2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>full</code> 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code> 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText</i> 218bee65486a185907111f3be60992433e133ec0e32Scott Russell * <td><code>new SimpleDateFormat(argStyleText, getLocale())</code> 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>spellout</code> 2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText (optional)</i> 2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.SPELLOUT) 223bee65486a185907111f3be60992433e133ec0e32Scott Russell * <br> .setDefaultRuleset(argStyleText);</code> 2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>ordinal</code> 2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText (optional)</i> 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.ORDINAL) 228bee65486a185907111f3be60992433e133ec0e32Scott Russell * <br> .setDefaultRuleset(argStyleText);</code> 2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>duration</code> 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>argStyleText (optional)</i> 2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.DURATION) 233bee65486a185907111f3be60992433e133ec0e32Scott Russell * <br> .setDefaultRuleset(argStyleText);</code> 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </table> 2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4><a name="diffsjdk">Differences from java.text.MessageFormat</a></h4> 2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>The ICU MessageFormat supports both named and numbered arguments, 2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * while the JDK MessageFormat only supports numbered arguments. 2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Named arguments make patterns more readable. 2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>ICU implements a more user-friendly apostrophe quoting syntax. 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In message text, an apostrophe only begins quoting literal text 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * if it immediately precedes a syntax character (mostly {curly braces}).<br> 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In the JDK MessageFormat, an apostrophe always begins quoting, 2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * which requires common text like "don't" and "aujourd'hui" 2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to be written with doubled apostrophes like "don''t" and "aujourd''hui". 2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For more details see {@link MessagePattern.ApostropheMode}. 2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>ICU does not create a ChoiceFormat object for a choiceArg, pluralArg or selectArg 2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * but rather handles such arguments itself. 2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The JDK MessageFormat does create and use a ChoiceFormat object 2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (<code>new ChoiceFormat(argStyleText)</code>). 2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The JDK does not support plural and select arguments at all. 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4>Usage Information</h4> 2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Here are some examples of usage: 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote> 2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Object[] arguments = { 2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 7, 2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * new Date(System.currentTimeMillis()), 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "a disturbance in the Force" 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * }; 2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * String result = MessageFormat.format( 2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", 2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments); 2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>output</em>: At 12:30 PM on Jul 3, 2053, there was a disturbance 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the Force on planet 7. 2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre> 2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </blockquote> 2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Typically, the message format will come from resources, and the 2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments will be dynamically set at runtime. 2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Example 2: 2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote> 2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> 2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Object[] testArgs = { 3, "MyDisk" }; 2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * MessageFormat form = new MessageFormat( 2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "The disk \"{1}\" contains {0} file(s)."); 2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * System.out.println(form.format(testArgs)); 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * // output, with different testArgs 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>output</em>: The disk "MyDisk" contains 0 file(s). 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>output</em>: The disk "MyDisk" contains 1 file(s). 2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>output</em>: The disk "MyDisk" contains 1,273 file(s). 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre> 2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </blockquote> 2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>For messages that include plural forms, you can use a plural argument: 2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> 2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * MessageFormat msgFmt = new MessageFormat( 2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "{num_files, plural, " + 3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "=0{There are no files on disk \"{disk_name}\".}" + 3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "=1{There is one file on disk \"{disk_name}\".}" + 3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "other{There are # files on disk \"{disk_name}\".}}", 3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * ULocale.ENGLISH); 3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Map args = new HashMap(); 3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * args.put("num_files", 0); 3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * args.put("disk_name", "MyDisk"); 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * System.out.println(msgFmt.format(args)); 3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * args.put("num_files", 3); 3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * System.out.println(msgFmt.format(args)); 3102d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * 3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>output</em>: 3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are no files on disk "MyDisk". 3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are 3 files on "MyDisk". 3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre> 3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * See {@link PluralFormat} and {@link PluralRules} for details. 3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <h4><a name="synchronization">Synchronization</a></h4> 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>MessageFormats are not synchronized. 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * It is recommended to create separate format instances for each thread. 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If multiple threads access a format concurrently, it must be synchronized 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * externally. 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see java.util.Locale 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see Format 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see NumberFormat 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see DecimalFormat 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see ChoiceFormat 3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see PluralFormat 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see SelectFormat 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Mark Davis 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author Markus Scherer 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class MessageFormat extends UFormat { 3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Incremented by 1 for ICU 4.8's new format. 3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert static final long serialVersionUID = 7136212545847378652L; 3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Constructs a MessageFormat for the default <code>FORMAT</code> locale and the 3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * specified pattern. 3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the locale and calls applyPattern(pattern). 3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern the pattern for this message format 3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception IllegalArgumentException if the pattern is invalid 3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see Category#FORMAT 3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public MessageFormat(String pattern) { 3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.ulocale = ULocale.getDefault(Category.FORMAT); 3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert applyPattern(pattern); 3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Constructs a MessageFormat for the specified locale and 3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern. 3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the locale and calls applyPattern(pattern). 3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern the pattern for this message format 3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale the locale for this message format 3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception IllegalArgumentException if the pattern is invalid 3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public MessageFormat(String pattern, Locale locale) { 3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this(pattern, ULocale.forLocale(locale)); 3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Constructs a MessageFormat for the specified locale and 3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern. 3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the locale and calls applyPattern(pattern). 3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern the pattern for this message format 3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale the locale for this message format 3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception IllegalArgumentException if the pattern is invalid 3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.2 3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public MessageFormat(String pattern, ULocale locale) { 3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.ulocale = locale; 3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert applyPattern(pattern); 3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the locale to be used for creating argument Format objects. 3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This affects subsequent calls to the {@link #applyPattern applyPattern} 3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method as well as to the <code>format</code> and 3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #formatToCharacterIterator formatToCharacterIterator} methods. 3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale the locale to be used when creating or comparing subformats 3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setLocale(Locale locale) { 3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setLocale(ULocale.forLocale(locale)); 3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the locale to be used for creating argument Format objects. 3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This affects subsequent calls to the {@link #applyPattern applyPattern} 4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method as well as to the <code>format</code> and 4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #formatToCharacterIterator formatToCharacterIterator} methods. 4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale the locale to be used when creating or comparing subformats 4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.2 4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setLocale(ULocale locale) { 4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* Save the pattern, and then reapply so that */ 4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* we pick up any changes in locale specific */ 4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* elements */ 4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String existingPattern = toPattern(); /*ibm.3550*/ 4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.ulocale = locale; 4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Invalidate all stock formatters. They are no longer valid since 4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // the locale has changed. 4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockDateFormatter = null; 4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockNumberFormatter = null; 4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pluralProvider = null; 4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ordinalProvider = null; 4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert applyPattern(existingPattern); /*ibm.3550*/ 4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the locale that's used when creating or comparing subformats. 4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the locale used when creating or comparing subformats 4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Locale getLocale() { 4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return ulocale.toLocale(); 4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Returns the locale that's used when creating argument Format objects. 4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the locale used when creating or comparing subformats 4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.2 4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public ULocale getULocale() { 4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return ulocale; 4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the pattern used by this message format. 4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses the pattern and caches Format objects for simple argument types. 4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Patterns and their interpretation are specified in the 4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <a href="#patterns">class description</a>. 4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pttrn the pattern for this message format 4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if the pattern is invalid 4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void applyPattern(String pttrn) { 4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null) { 4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern = new MessagePattern(pttrn); 4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern.parse(pttrn); 4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Cache the formats that are explicitly mentioned in the message pattern. 4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert cacheExplicitFormats(); 4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(RuntimeException e) { 4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert resetPattern(); 4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw e; 4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Sets the ApostropheMode and the pattern used by this message format. 4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses the pattern and caches Format objects for simple argument types. 4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Patterns and their interpretation are specified in the 4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <a href="#patterns">class description</a>. 4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is best used only once on a given object to avoid confusion about the mode, 4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and after constructing the object with an empty pattern string to minimize overhead. 4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern the pattern for this message format 4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param aposMode the new ApostropheMode 4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if the pattern is invalid 4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see MessagePattern.ApostropheMode 4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.8 4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void applyPattern(String pattern, MessagePattern.ApostropheMode aposMode) { 4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null) { 4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern = new MessagePattern(aposMode); 4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (aposMode != msgPattern.getApostropheMode()) { 4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern.clearPatternAndSetApostropheMode(aposMode); 4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert applyPattern(pattern); 4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} 4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return this instance's ApostropheMode. 4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.8 4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public MessagePattern.ApostropheMode getApostropheMode() { 4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null) { 4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern = new MessagePattern(); // Sets the default mode. 4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return msgPattern.getApostropheMode(); 5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the applied pattern string. 5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the pattern string 5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalStateException after custom Format objects have been set 5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * via setFormat() or similar APIs 5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String toPattern() { 5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Return the original, applied pattern string, or else "". 5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Note: This does not take into account 5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // - changes from setFormat() and similar methods, or 5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // - normalization of apostrophes and arguments, for example, 5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // whether some date/time/number formatter was created via a pattern 5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // but is equivalent to the "medium" default format. 5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (customFormatArgStarts != null) { 5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalStateException( 5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "toPattern() is not supported after custom Format objects "+ 5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "have been set via setFormat() or similar APIs"); 5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null) { 5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return ""; 5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String originalPattern = msgPattern.getPatternString(); 5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return originalPattern == null ? "" : originalPattern; 5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the part index of the next ARG_START after partIndex, or -1 if there is none more. 5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param partIndex Part index of the previous ARG_START (initially 0). 5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int nextTopLevelArgStart(int partIndex) { 5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (partIndex != 0) { 5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex = msgPattern.getLimitPartIndex(partIndex); 5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (;;) { 5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern.Part.Type type = msgPattern.getPartType(++partIndex); 5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type == MessagePattern.Part.Type.ARG_START) { 5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return partIndex; 5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type == MessagePattern.Part.Type.MSG_LIMIT) { 5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; 5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private boolean argNameMatches(int partIndex, String argName, int argNumber) { 5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part = msgPattern.getPart(partIndex); 5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return part.getType() == MessagePattern.Part.Type.ARG_NAME ? 5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern.partSubstringMatches(part, argName) : 5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert part.getValue() == argNumber; // ARG_NUMBER 5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String getArgName(int partIndex) { 5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part = msgPattern.getPart(partIndex); 5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (part.getType() == MessagePattern.Part.Type.ARG_NAME) { 5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return msgPattern.getSubstring(part); 5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return Integer.toString(part.getValue()); 5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the Format objects to use for the values passed into 5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>format</code> methods or returned from <code>parse</code> 5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * methods. The indices of elements in <code>newFormats</code> 5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * correspond to the argument indices used in the previously set 5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern string. 5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The order of formats in <code>newFormats</code> thus corresponds to 5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the order of elements in the <code>arguments</code> array passed 5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to the <code>format</code> methods or the result array returned 5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by the <code>parse</code> methods. 5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If an argument index is used for more than one format element 5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the pattern string, then the corresponding new format is used 5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for all such format elements. If an argument index is not used 5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for any format element in the pattern string, then the 5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * corresponding new format is ignored. If fewer formats are provided 5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * than needed, then only the formats for argument indices less 5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * than <code>newFormats.length</code> are replaced. 5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is only supported if the format does not use 5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * named arguments, otherwise an IllegalArgumentException is thrown. 5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormats the new formats to use 5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws NullPointerException if <code>newFormats</code> is null 5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this formatter uses named arguments 5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormatsByArgumentIndex(Format[] newFormats) { 5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern.hasNamedArguments()) { 5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException( 5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "This method is not available in MessageFormat objects " + 5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "that use alphanumeric argument names."); 5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber = msgPattern.getPart(partIndex + 1).getValue(); 5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNumber < newFormats.length) { 5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormats[argNumber]); 6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Sets the Format objects to use for the values passed into 6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>format</code> methods or returned from <code>parse</code> 6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * methods. The keys in <code>newFormats</code> are the argument 6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * names in the previously set pattern string, and the values 6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * are the formats. 6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Only argument names from the pattern string are considered. 6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Extra keys in <code>newFormats</code> that do not correspond 6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to an argument name are ignored. Similarly, if there is no 6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format in newFormats for an argument name, the formatter 6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for that argument remains unchanged. 6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This may be called on formats that do not use named arguments. 6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In this case the map will be queried for key Strings that 6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * represent argument indices, e.g. "0", "1", "2" etc. 6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormats a map from String to Format providing new 6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * formats for named arguments. 6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormatsByArgumentName(Map<String, Format> newFormats) { 6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String key = getArgName(partIndex + 1); 6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (newFormats.containsKey(key)) { 6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormats.get(key)); 6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the Format objects to use for the format elements in the 6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previously set pattern string. 6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The order of formats in <code>newFormats</code> corresponds to 6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the order of format elements in the pattern string. 6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If more formats are provided than needed by the pattern string, 6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the remaining ones are ignored. If fewer formats are provided 6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * than needed, then only the first <code>newFormats.length</code> 6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * formats are replaced. 6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Since the order of format elements in a pattern string often 6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * changes during localization, it is generally better to use the 6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex} 6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method, which assumes an order of formats corresponding to the 6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * order of elements in the <code>arguments</code> array passed to 6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the <code>format</code> methods or the result array returned by 6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the <code>parse</code> methods. 6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormats the new formats to use 6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception NullPointerException if <code>newFormats</code> is null 6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormats(Format[] newFormats) { 6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int formatNumber = 0; 6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; 6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatNumber < newFormats.length && 6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormats[formatNumber]); 6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++formatNumber; 6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the Format object to use for the format elements within the 6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previously set pattern string that use the given argument 6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * index. 6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The argument index is part of the format element definition and 6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * represents an index into the <code>arguments</code> array passed 6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to the <code>format</code> methods or the result array returned 6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by the <code>parse</code> methods. 6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the argument index is used for more than one format element 6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the pattern string, then the new format is used for all such 6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format elements. If the argument index is not used for any format 6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * element in the pattern string, then the new format is ignored. 6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is only supported when exclusively numbers are used for 6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument names. Otherwise an IllegalArgumentException is thrown. 6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param argumentIndex the argument index for which to use the new format 6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormat the new format to use 6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) { 6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern.hasNamedArguments()) { 6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException( 6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "This method is not available in MessageFormat objects " + 6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "that use alphanumeric argument names."); 6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern.getPart(partIndex + 1).getValue() == argumentIndex) { 6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormat); 6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Sets the Format object to use for the format elements within the 7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previously set pattern string that use the given argument 7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * name. 7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the argument name is used for more than one format element 7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the pattern string, then the new format is used for all such 7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format elements. If the argument name is not used for any format 7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * element in the pattern string, then the new format is ignored. 7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This API may be used on formats that do not use named arguments. 7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In this case <code>argumentName</code> should be a String that names 7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * an argument index, e.g. "0", "1", "2"... etc. If it does not name 7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a valid index, the format will be ignored. No error is thrown. 7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param argumentName the name of the argument to change 7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormat the new format to use 7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormatByArgumentName(String argumentName, Format newFormat) { 7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber = MessagePattern.validateArgumentName(argumentName); 7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) { 7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNameMatches(partIndex + 1, argumentName, argNumber)) { 7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormat); 7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets the Format object to use for the format element with the given 7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format element index within the previously set pattern string. 7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The format element index is the zero-based number of the format 7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * element counting from the start of the pattern string. 7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Since the order of format elements in a pattern string often 7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * changes during localization, it is generally better to use the 7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #setFormatByArgumentIndex setFormatByArgumentIndex} 7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method, which accesses format elements based on the argument 7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * index they specify. 7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param formatElementIndex the index of a format element within the pattern 7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param newFormat the format to use for the specified format element 7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or 7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * larger than the number of format elements in the pattern string 7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setFormat(int formatElementIndex, Format newFormat) { 7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int formatNumber = 0; 7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (formatNumber == formatElementIndex) { 7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setCustomArgStartFormat(partIndex, newFormat); 7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++formatNumber; 7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ArrayIndexOutOfBoundsException(formatElementIndex); 7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the Format objects used for the values passed into 7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>format</code> methods or returned from <code>parse</code> 7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * methods. The indices of elements in the returned array 7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * correspond to the argument indices used in the previously set 7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern string. 7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The order of formats in the returned array thus corresponds to 7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the order of elements in the <code>arguments</code> array passed 7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to the <code>format</code> methods or the result array returned 7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by the <code>parse</code> methods. 7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If an argument index is used for more than one format element 7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the pattern string, then the format used for the last such 7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format element is returned in the array. If an argument index 7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is not used for any format element in the pattern string, then 7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * null is returned in the array. 7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is only supported when exclusively numbers are used for 7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument names. Otherwise an IllegalArgumentException is thrown. 7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the formats used for the arguments within the pattern 7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Format[] getFormatsByArgumentIndex() { 7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern.hasNamedArguments()) { 7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException( 7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "This method is not available in MessageFormat objects " + 7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "that use alphanumeric argument names."); 7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArrayList<Format> list = new ArrayList<Format>(); 7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber = msgPattern.getPart(partIndex + 1).getValue(); 7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (argNumber >= list.size()) { 7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert list.add(null); 7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert list.set(argNumber, cachedFormatters == null ? null : cachedFormatters.get(partIndex)); 8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return list.toArray(new Format[list.size()]); 8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the Format objects used for the format elements in the 8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * previously set pattern string. 8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The order of formats in the returned array corresponds to 8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the order of format elements in the pattern string. 8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Since the order of format elements in a pattern string often 8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * changes during localization, it's generally better to use the 8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #getFormatsByArgumentIndex()} 8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * method, which assumes an order of formats corresponding to the 8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * order of elements in the <code>arguments</code> array passed to 8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the <code>format</code> methods or the result array returned by 8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the <code>parse</code> methods. 8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is only supported when exclusively numbers are used for 8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument names. Otherwise an IllegalArgumentException is thrown. 8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the formats used for the format elements in the pattern 8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Format[] getFormats() { 8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArrayList<Format> list = new ArrayList<Format>(); 8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert list.add(cachedFormatters == null ? null : cachedFormatters.get(partIndex)); 8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return list.toArray(new Format[list.size()]); 8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Returns the top-level argument names. For more details, see 8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@link #setFormatByArgumentName(String, Format)}. 8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return a Set of argument names 8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.8 8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Set<String> getArgumentNames() { 8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Set<String> result = new HashSet<String>(); 8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.add(getArgName(partIndex + 1)); 8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Returns the first top-level format associated with the given argument name. 8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For more details, see {@link #setFormatByArgumentName(String, Format)}. 8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param argumentName The name of the desired argument. 8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the Format associated with the name, or null if there isn't one. 8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.8 8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Format getFormatByArgumentName(String argumentName) { 8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (cachedFormatters == null) { 8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber = MessagePattern.validateArgumentName(argumentName); 8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) { 8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNameMatches(partIndex + 1, argumentName, argNumber)) { 8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return cachedFormatters.get(partIndex); 8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats an array of objects and appends the <code>MessageFormat</code>'s 8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern, with arguments replaced by the formatted objects, to the 8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * provided <code>StringBuffer</code>. 8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The text substituted for the individual format elements is derived from 8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the current subformat of the format element and the 8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> element at the format element's argument index 8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * as indicated by the first matching line of the following table. An 8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument is <i>unavailable</i> if <code>arguments</code> is 8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>null</code> or has fewer than argumentIndex+1 elements. When 8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * an argument is unavailable no substitution is performed. 882bee65486a185907111f3be60992433e133ec0e32Scott Russell * 8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <table border=1> 8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>argType or Format 8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>value object 8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <th>Formatted Text 8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>any</i> 8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>unavailable</i> 8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>"{" + argNameOrNumber + "}"</code> 8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>any</i> 8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>null</code> 8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>"null"</code> 8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>custom Format <code>!= null</code> 8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>any</i> 8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>customFormat.format(argument)</code> 9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>noneArg, or custom Format <code>== null</code> 9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>instanceof Number</code> 9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code> 9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>noneArg, or custom Format <code>== null</code> 9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>instanceof Date</code> 9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, 9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateFormat.SHORT, getLocale()).format(argument)</code> 9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>noneArg, or custom Format <code>== null</code> 9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>instanceof String</code> 9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>argument</code> 9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>noneArg, or custom Format <code>== null</code> 9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>any</i> 9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><code>argument.toString()</code> 9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <tr> 9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>complexArg 9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td><i>any</i> 9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <td>result of recursive formatting of a selected sub-message 9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </table> 9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If <code>pos</code> is non-null, and refers to 9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>Field.ARGUMENT</code>, the location of the first formatted 9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * string will be returned. 9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This method is only supported when the format does not use named 9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments, otherwise an IllegalArgumentException is thrown. 9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param arguments an array of objects to be formatted and substituted. 9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param result where text is appended. 9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos On input: an alignment field, if desired. 9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * On output: the offsets of the alignment field. 9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if a value in the 9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> array is not of the type 9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the corresponding argument or custom Format object. 9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public final StringBuffer format(Object[] arguments, StringBuffer result, 9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FieldPosition pos) 9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(arguments, null, new AppendableWrapper(result), pos); 9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats a map of objects and appends the <code>MessageFormat</code>'s 9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern, with arguments replaced by the formatted objects, to the 9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * provided <code>StringBuffer</code>. 9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The text substituted for the individual format elements is derived from 9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the current subformat of the format element and the 9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> value corresopnding to the format element's 9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument name. 9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A numbered pattern argument is matched with a map key that contains that number 9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * as an ASCII-decimal-digit string (without leading zero). 9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * An argument is <i>unavailable</i> if <code>arguments</code> is 9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>null</code> or does not have a value corresponding to an argument 9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * name in the pattern. When an argument is unavailable no substitution 9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is performed. 9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param arguments a map of objects to be formatted and substituted. 9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param result where text is appended. 9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos On input: an alignment field, if desired. 9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * On output: the offsets of the alignment field. 9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if a value in the 9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> array is not of the type 9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the corresponding argument or custom Format object. 9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the passed-in StringBuffer 9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public final StringBuffer format(Map<String, Object> arguments, StringBuffer result, 9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FieldPosition pos) { 9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(null, arguments, new AppendableWrapper(result), pos); 9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Creates a MessageFormat with the given pattern and uses it 9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to format the given arguments. This is equivalent to 9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote> 9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link 9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) 9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format}(arguments, new StringBuffer(), null).toString()</code> 9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </blockquote> 9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if the pattern is invalid 9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if a value in the 9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> array is not of the type 9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the corresponding argument or custom Format object. 9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static String format(String pattern, Object... arguments) { 9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat temp = new MessageFormat(pattern); 9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return temp.format(arguments); 10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Creates a MessageFormat with the given pattern and uses it to 10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * format the given arguments. The pattern must identifyarguments 10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by name instead of by number. 10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if the pattern is invalid 10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if a value in the 10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> array is not of the type 10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the corresponding argument or custom Format object. 10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see #format(Map, StringBuffer, FieldPosition) 10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see #format(String, Object[]) 10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static String format(String pattern, Map<String, Object> arguments) { 10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat temp = new MessageFormat(pattern); 10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return temp.format(arguments); 10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Returns true if this MessageFormat uses named arguments, 10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and false otherwise. See class description. 10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return true if named arguments are used. 10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean usesNamedArguments() { 10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return msgPattern.hasNamedArguments(); 10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Overrides 10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats a map or array of objects and appends the <code>MessageFormat</code>'s 10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern, with format elements replaced by the formatted objects, to the 10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * provided <code>StringBuffer</code>. 10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This is equivalent to either of 10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote> 10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, 10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code> 10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>{@link #format(java.util.Map, java.lang.StringBuffer, 10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * java.text.FieldPosition) format}((Map) arguments, result, pos)</code> 10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </blockquote> 10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A map must be provided if this format uses named arguments, otherwise 10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * an IllegalArgumentException will be thrown. 10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param arguments a map or array of objects to be formatted 10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param result where text is appended 10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos On input: an alignment field, if desired 10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * On output: the offsets of the alignment field 10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if an argument in 10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> is not of the type 10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the format element(s) that use it 1052bee65486a185907111f3be60992433e133ec0e32Scott Russell * @throws IllegalArgumentException if <code>arguments</code> is 10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * an array of Object and this format uses named arguments 10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 10562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public final StringBuffer format(Object arguments, StringBuffer result, 10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FieldPosition pos) 10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(arguments, new AppendableWrapper(result), pos); 10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats an array of objects and inserts them into the 10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>MessageFormat</code>'s pattern, producing an 10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>AttributedCharacterIterator</code>. 10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * You can use the returned <code>AttributedCharacterIterator</code> 10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to build the resulting String, as well as to determine information 10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * about the resulting String. 10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The text of the returned <code>AttributedCharacterIterator</code> is 10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the same that would be returned by 10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <blockquote> 10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, 10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code> 10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </blockquote> 10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In addition, the <code>AttributedCharacterIterator</code> contains at 10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * least attributes indicating where text was generated from an 10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument in the <code>arguments</code> array. The keys of these attributes are of 10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * type <code>MessageFormat.Field</code>, their values are 10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>Integer</code> objects indicating the index in the <code>arguments</code> 10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * array of the argument from which the text was generated. 10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The attributes/value from the underlying <code>Format</code> 10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * instances that <code>MessageFormat</code> uses will also be 10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * placed in the resulting <code>AttributedCharacterIterator</code>. 10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This allows you to not only find where an argument is placed in the 10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * resulting String, but also which fields it contains in turn. 10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param arguments an array of objects to be formatted and substituted. 10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return AttributedCharacterIterator describing the formatted value. 10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception NullPointerException if <code>arguments</code> is null. 10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if a value in the 10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> array is not of the type 10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the corresponding argument or custom Format object. 10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 11002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public AttributedCharacterIterator formatToCharacterIterator(Object arguments) { 11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (arguments == null) { 11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new NullPointerException( 11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "formatToCharacterIterator must be passed non-null object"); 11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder result = new StringBuilder(); 11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AppendableWrapper wrapper = new AppendableWrapper(result); 11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert wrapper.useAttributes(); 11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(arguments, wrapper, null); 11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AttributedString as = new AttributedString(result.toString()); 11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (AttributeAndPosition a : wrapper.attributes) { 11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert as.addAttribute(a.key, a.value, a.start, a.limit); 11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return as.getIterator(); 11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses the string. 11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Caveats: The parse may fail in a number of circumstances. 11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example: 11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ul> 11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>If one of the arguments does not occur in the pattern. 11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>If the format of an argument loses information, such as 11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with a choice format where a large number formats to "many". 11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>Does not yet handle recursion (where 11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the substituted strings contain {n} references.) 11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>Will not always find a match (or the correct match) 11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * if some part of the parse is ambiguous. 11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if the pattern "{1},{2}" is used with the 11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * string arguments {"a,b", "c"}, it will format as "a,b,c". 11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * When the result is parsed, it will return {"a", "b,c"}. 11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>If a single argument is parsed more than once in the string, 11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * then the later parse wins. 11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ul> 11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * When the parse fails, use ParsePosition.getErrorIndex() to find out 11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * where in the string did the parsing failed. The returned error 11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * index is the starting offset of the sub-patterns that the string 11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is comparing with. For example, if the parsing string "AAA {0} BBB" 11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is comparing against the pattern "AAD {0} BBB", the error index is 11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 0. When an error occurs, the call to this method will return null. 11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the source is null, return an empty array. 11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if this format uses named arguments 11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Object[] parse(String source, ParsePosition pos) { 11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern.hasNamedArguments()) { 11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException( 11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "This method is not available in MessageFormat objects " + 11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "that use named argument."); 11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Count how many slots we need in the array. 11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int maxArgId = -1; 11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber=msgPattern.getPart(partIndex + 1).getValue(); 11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (argNumber > maxArgId) { 11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert maxArgId = argNumber; 11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object[] resultArray = new Object[maxArgId + 1]; 11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int backupStartPos = pos.getIndex(); 11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert parse(0, source, pos, resultArray, null); 11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pos.getIndex() == backupStartPos) { // unchanged, returned object is null 11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return resultArray; 11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11722d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Parses the string, returning the results in a Map. 11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This is similar to the version that returns an array 11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * of Object. This supports both named and numbered 11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments-- if numbered, the keys in the map are the 11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...). 11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param source the text to parse 11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos the position at which to start parsing. on return, 11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * contains the result of the parse. 11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return a Map containing key/value pairs for each parsed argument. 11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Map<String, Object> parseToMap(String source, ParsePosition pos) { 11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Map<String, Object> result = new HashMap<String, Object>(); 11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int backupStartPos = pos.getIndex(); 11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert parse(0, source, pos, null, result); 11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pos.getIndex() == backupStartPos) { 11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert return result; 11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 11952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses text from the beginning of the given string to produce an object 11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * array. 11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The method may not use the entire text of the given string. 12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * See the {@link #parse(String, ParsePosition)} method for more information 12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * on message parsing. 12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param source A <code>String</code> whose beginning should be parsed. 12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return An <code>Object</code> array parsed from the string. 12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception ParseException if the beginning of the specified string cannot be parsed. 12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @exception IllegalArgumentException if this format uses named arguments 12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Object[] parse(String source) throws ParseException { 12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ParsePosition pos = new ParsePosition(0); 12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object[] result = parse(source, pos); 12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pos.getIndex() == 0) // unchanged, returned object is null 12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ParseException("MessageFormat parse error!", 12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.getErrorIndex()); 12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses the string, filling either the Map or the Array. 12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This is a private method that all the public parsing methods call. 12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This supports both named and numbered 12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments-- if numbered, the keys in the map are the 12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...). 12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param msgStart index in the message pattern to start from. 12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param source the text to parse 12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos the position at which to start parsing. on return, 12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * contains the result of the parse. 12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param args if not null, the parse results will be filled here (The pattern 12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * has to have numbered arguments in order for this to not be null). 12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param argsMap if not null, the parse results will be filled here. 12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void parse(int msgStart, String source, ParsePosition pos, 12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object[] args, Map<String, Object> argsMap) { 12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (source == null) { 12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msgString=msgPattern.getPatternString(); 12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevIndex=msgPattern.getPart(msgStart).getLimit(); 12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int sourceOffset = pos.getIndex(); 12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ParsePosition tempStatus = new ParsePosition(0); 12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(int i=msgStart+1; ; ++i) { 12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part=msgPattern.getPart(i); 12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type=part.getType(); 12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index=part.getIndex(); 12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Make sure the literal string matches. 12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int len = index - prevIndex; 12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (len == 0 || msgString.regionMatches(prevIndex, source, sourceOffset, len)) { 12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sourceOffset += len; 12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex += len; 12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setErrorIndex(sourceOffset); 12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; // leave index as is to signal error 12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.MSG_LIMIT) { 12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Things went well! Done. 12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setIndex(sourceOffset); 12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR) { 12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex=part.getLimit(); 12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We do not support parsing Plural formats. (No REPLACE_NUMBER here.) 12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert type==Part.Type.ARG_START : "Unexpected Part "+part+" in parsed message."; 12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argLimit=msgPattern.getLimitPartIndex(i); 12702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 12717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArgType argType=part.getArgType(); 12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert part=msgPattern.getPart(++i); 12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Compute the argId, so we can use it as a key. 12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object argId=null; 12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber = 0; 12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String key = null; 12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(args!=null) { 12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argNumber=part.getValue(); // ARG_NUMBER 12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argId = Integer.valueOf(argNumber); 12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(part.getType()==MessagePattern.Part.Type.ARG_NAME) { 12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key=msgPattern.getSubstring(part); 12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else /* ARG_NUMBER */ { 12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key=Integer.toString(part.getValue()); 12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argId = key; 12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++i; 12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format formatter = null; 12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean haveArgResult = false; 12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object argResult = null; 12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) { 12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Just parse using the formatter. 12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert tempStatus.setIndex(sourceOffset); 12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argResult = formatter.parseObject(source, tempStatus); 12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (tempStatus.getIndex() == sourceOffset) { 12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setErrorIndex(sourceOffset); 12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; // leave index as is to signal error 13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert haveArgResult = true; 13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sourceOffset = tempStatus.getIndex(); 13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if( 13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argType==ArgType.NONE || 13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) { 13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Match as a string. 13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // if at end, use longest possible match 13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // otherwise uses first match to intervening string 13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // does NOT recursively try all possibilities 13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String stringAfterArgument = getLiteralStringUntilNextArgument(argLimit); 13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int next; 13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (stringAfterArgument.length() != 0) { 13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert next = source.indexOf(stringAfterArgument, sourceOffset); 13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert next = source.length(); 13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (next < 0) { 13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setErrorIndex(sourceOffset); 13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; // leave index as is to signal error 13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String strValue = source.substring(sourceOffset, next); 13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!strValue.equals("{" + argId.toString() + "}")) { 13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert haveArgResult = true; 13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argResult = strValue; 13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sourceOffset = next; 13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(argType==ArgType.CHOICE) { 13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert tempStatus.setIndex(sourceOffset); 13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double choiceResult = parseChoiceArgument(msgPattern, i, source, tempStatus); 13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (tempStatus.getIndex() == sourceOffset) { 13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setErrorIndex(sourceOffset); 13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; // leave index as is to signal error 13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argResult = choiceResult; 13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert haveArgResult = true; 13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sourceOffset = tempStatus.getIndex(); 13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(argType.hasPluralStyle() || argType==ArgType.SELECT) { 13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // No can do! 13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new UnsupportedOperationException( 13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "Parsing of plural/select/selectordinal argument is not supported."); 13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This should never happen. 13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalStateException("unexpected argType "+argType); 13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (haveArgResult) { 13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (args != null) { 13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert args[argNumber] = argResult; 13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (argsMap != null) { 13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argsMap.put(key, argResult); 13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex=msgPattern.getPart(argLimit).getLimit(); 13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert i=argLimit; 13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Parses text from the beginning of the given string to produce a map from 13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument to values. The method may not use the entire text of the given string. 13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>See the {@link #parse(String, ParsePosition)} method for more information on 13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * message parsing. 13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param source A <code>String</code> whose beginning should be parsed. 13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return A <code>Map</code> parsed from the string. 13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws ParseException if the beginning of the specified string cannot 13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * be parsed. 13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see #parseToMap(String, ParsePosition) 13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Map<String, Object> parseToMap(String source) throws ParseException { 13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ParsePosition pos = new ParsePosition(0); 13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Map<String, Object> result = new HashMap<String, Object>(); 13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert parse(0, source, pos, null, result); 13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pos.getIndex() == 0) // unchanged, returned object is null 13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ParseException("MessageFormat parse error!", 13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.getErrorIndex()); 13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Parses text from a string to produce an object array or Map. 13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The method attempts to parse text starting at the index given by 13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>pos</code>. 13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If parsing succeeds, then the index of <code>pos</code> is updated 13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to the index after the last character used (parsing does not necessarily 13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * use all characters up to the end of the string), and the parsed 13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * object array is returned. The updated <code>pos</code> can be used to 13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * indicate the starting point for the next call to this method. 13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If an error occurs, then the index of <code>pos</code> is not 13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * changed, the error index of <code>pos</code> is set to the index of 13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the character where the error occurred, and null is returned. 13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * See the {@link #parse(String, ParsePosition)} method for more information 13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * on message parsing. 13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param source A <code>String</code>, part of which should be parsed. 14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pos A <code>ParsePosition</code> object with index and error 14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * index information as described above. 14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return An <code>Object</code> parsed from the string, either an 14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * array of Object, or a Map, depending on whether named 14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * arguments are used. This can be queried using <code>usesNamedArguments</code>. 14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * In case of error, returns null. 14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws NullPointerException if <code>pos</code> is null. 14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 14102d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Object parseObject(String source, ParsePosition pos) { 14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!msgPattern.hasNamedArguments()) { 14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return parse(source, pos); 14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return parseToMap(source, pos); 14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public Object clone() { 14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat other = (MessageFormat) super.clone(); 14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (customFormatArgStarts != null) { 14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.customFormatArgStarts = new HashSet<Integer>(); 14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (Integer key : customFormatArgStarts) { 14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.customFormatArgStarts.add(key); 14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.customFormatArgStarts = null; 14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14352d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (cachedFormatters != null) { 14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.cachedFormatters = new HashMap<Integer, Format>(); 14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Iterator<Map.Entry<Integer, Format>> it = cachedFormatters.entrySet().iterator(); 14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (it.hasNext()){ 14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Map.Entry<Integer, Format> entry = it.next(); 14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.cachedFormatters.put(entry.getKey(), entry.getValue()); 14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.cachedFormatters = null; 14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.msgPattern = msgPattern == null ? null : (MessagePattern)msgPattern.clone(); 14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.stockDateFormatter = 14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockDateFormatter == null ? null : (DateFormat) stockDateFormatter.clone(); 14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.stockNumberFormatter = 14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockNumberFormatter == null ? null : (NumberFormat) stockNumberFormatter.clone(); 14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.pluralProvider = null; 14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert other.ordinalProvider = null; 14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return other; 14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean equals(Object obj) { 14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this == obj) // quick check 14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return true; 14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (obj == null || getClass() != obj.getClass()) 14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat other = (MessageFormat) obj; 14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return Utility.objectEquals(ulocale, other.ulocale) 14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert && Utility.objectEquals(msgPattern, other.msgPattern) 14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert && Utility.objectEquals(cachedFormatters, other.cachedFormatters) 14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert && Utility.objectEquals(customFormatArgStarts, other.customFormatArgStarts); 14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Note: It might suffice to only compare custom formatters 14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // rather than all formatters. 14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@inheritDoc} 14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.0 14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int hashCode() { 14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return msgPattern.getPatternString().hashCode(); // enough for reasonable distribution 14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Defines constants that are used as attribute keys in the 14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>AttributedCharacterIterator</code> returned 14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from <code>MessageFormat.formatToCharacterIterator</code>. 14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static class Field extends Format.Field { 14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final long serialVersionUID = 7510380454602616157L; 14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Create a <code>Field</code> with the specified name. 14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param name The name of the attribute 15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert protected Field(String name) { 15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert super(name); 15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Resolves instances being deserialized to the predefined constants. 15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return resolved MessageFormat.Field constant 15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws InvalidObjectException if the constant could not be resolved. 15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15162d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert protected Object readResolve() throws InvalidObjectException { 15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this.getClass() != MessageFormat.Field.class) { 15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new InvalidObjectException( 15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "A subclass of MessageFormat.Field must implement readResolve."); 15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (this.getName().equals(ARGUMENT.getName())) { 15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return ARGUMENT; 15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new InvalidObjectException("Unknown attribute name."); 15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Constant identifying a portion of a message that was generated 15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from an argument passed into <code>formatToCharacterIterator</code>. 15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The value associated with the key will be an <code>Integer</code> 15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * indicating the index in the <code>arguments</code> array of the 15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * argument from which the text was generated. 15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.8 15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static final Field ARGUMENT = new Field("message argument field"); 15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ===========================privates============================ 15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // *Important*: All fields must be declared *transient* so that we can fully 15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // control serialization! 15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // See for example Joshua Bloch's "Effective Java", chapter 10 Serialization. 15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The locale to use for formatting numbers and dates. 15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient ULocale ulocale; 15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The MessagePattern which contains the parsed structure of the pattern string. 15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient MessagePattern msgPattern; 15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Cached formatters so we can just use them whenever needed instead of creating 15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * them from scratch every time. 15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient Map<Integer, Format> cachedFormatters; 15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Set of ARG_START part indexes where custom, user-provided Format objects 15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * have been set via setFormat() or similar API. 15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient Set<Integer> customFormatArgStarts; 15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Stock formatters. Those are used when a format is not explicitly mentioned in 15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the message. The format is inferred from the argument. 15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient DateFormat stockDateFormatter; 15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient NumberFormat stockNumberFormatter; 15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient PluralSelectorProvider pluralProvider; 15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient PluralSelectorProvider ordinalProvider; 15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private DateFormat getStockDateFormatter() { 15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (stockDateFormatter == null) { 15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockDateFormatter = DateFormat.getDateTimeInstance( 15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DateFormat.SHORT, DateFormat.SHORT, ulocale);//fix 15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return stockDateFormatter; 15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private NumberFormat getStockNumberFormatter() { 15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (stockNumberFormatter == null) { 15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert stockNumberFormatter = NumberFormat.getInstance(ulocale); 15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return stockNumberFormatter; 15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // *Important*: All fields must be declared *transient*. 15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // See the longer comment above ulocale. 15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats the arguments and writes the result into the 15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * AppendableWrapper, updates the field position. 15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>Exactly one of args and argsMap must be null, the other non-null. 15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param msgStart Index to msgPattern part to start formatting from. 16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pluralNumber null except when formatting a plural argument sub-message 16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * where a '#' is replaced by the format string for this number. 16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param args The formattable objects array. Non-null iff numbered values are used. 16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param argsMap The key-value map of formattable objects. Non-null iff named values are used. 16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param dest Output parameter to receive the result. 16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The result (string & attributes) is appended to existing contents. 16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param fp Field position status. 16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void format(int msgStart, PluralSelectorContext pluralNumber, 16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object[] args, Map<String, Object> argsMap, 16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AppendableWrapper dest, FieldPosition fp) { 16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msgString=msgPattern.getPatternString(); 16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevIndex=msgPattern.getPart(msgStart).getLimit(); 16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(int i=msgStart+1;; ++i) { 16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part=msgPattern.getPart(i); 16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type=part.getType(); 16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index=part.getIndex(); 16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append(msgString, prevIndex, index); 16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.MSG_LIMIT) { 16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex=part.getLimit(); 16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.REPLACE_NUMBER) { 16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(pluralNumber.forReplaceNumber) { 16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // number-offset was already formatted. 16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(pluralNumber.formatter, 16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pluralNumber.number, pluralNumber.numberString); 16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(getStockNumberFormatter(), pluralNumber.number); 16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type!=Part.Type.ARG_START) { 16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argLimit=msgPattern.getLimitPartIndex(i); 16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArgType argType=part.getArgType(); 16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert part=msgPattern.getPart(++i); 16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object arg; 16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean noArg=false; 16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object argId=null; 16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String argName=msgPattern.getSubstring(part); 16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(args!=null) { 16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int argNumber=part.getValue(); // ARG_NUMBER 16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (dest.attributes != null) { 16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We only need argId if we add it into the attributes. 16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argId = Integer.valueOf(argNumber); 16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(0<=argNumber && argNumber<args.length) { 16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert arg=args[argNumber]; 16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert arg=null; 16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert noArg=true; 16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argId = argName; 16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(argsMap!=null && argsMap.containsKey(argName)) { 16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert arg=argsMap.get(argName); 16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert arg=null; 16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert noArg=true; 16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++i; 16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevDestLength=dest.length; 16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format formatter = null; 16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (noArg) { 16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append("{"+argName+"}"); 16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (arg == null) { 16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append("null"); 16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(pluralNumber!=null && pluralNumber.numberArgIndex==(i-2)) { 16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(pluralNumber.offset == 0) { 16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The number was already formatted with this formatter. 16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(pluralNumber.formatter, pluralNumber.number, pluralNumber.numberString); 16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Do not use the formatted (number-offset) string for a named argument 16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // that formats the number without subtracting the offset. 16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(pluralNumber.formatter, arg); 16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) { 16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. 16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ( formatter instanceof ChoiceFormat || 16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatter instanceof PluralFormat || 16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatter instanceof SelectFormat) { 16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We only handle nested formats here if they were provided via setFormat() or its siblings. 16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Otherwise they are not cached and instead handled below according to argType. 16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String subMsgString = formatter.format(arg); 16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (subMsgString.indexOf('{') >= 0 || 16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (subMsgString.indexOf('\'') >= 0 && !msgPattern.jdkAposMode())) { 16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat subMsgFormat = new MessageFormat(subMsgString, ulocale); 16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subMsgFormat.format(0, null, args, argsMap, dest, null); 16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (dest.attributes == null) { 16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append(subMsgString); 16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This formats the argument twice, once above to get the subMsgString 16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // and then once more here. 16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // It only happens in formatToCharacterIterator() 16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // on a complex Format set via setFormat(), 16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // and only when the selected subMsgString does not need further formatting. 17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This imitates ICU 4.6 behavior. 17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(formatter, arg); 17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(formatter, arg); 17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if( 17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argType==ArgType.NONE || 17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) { 17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ArgType.NONE, or 17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // any argument which got reset to null via setFormat() or its siblings. 17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (arg instanceof Number) { 17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // format number if can 17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(getStockNumberFormatter(), arg); 17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (arg instanceof Date) { 17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // format a Date if can 17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.formatAndAppend(getStockDateFormatter(), arg); 17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append(arg.toString()); 17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(argType==ArgType.CHOICE) { 17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!(arg instanceof Number)) { 17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("'" + arg + "' is not a Number"); 17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double number = ((Number)arg).doubleValue(); 17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int subMsgStart=findChoiceSubMessage(msgPattern, i, number); 17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatComplexSubMessage(subMsgStart, null, args, argsMap, dest); 17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(argType.hasPluralStyle()) { 17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!(arg instanceof Number)) { 17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("'" + arg + "' is not a Number"); 17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PluralSelectorProvider selector; 17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(argType == ArgType.PLURAL) { 17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (pluralProvider == null) { 17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pluralProvider = new PluralSelectorProvider(this, PluralType.CARDINAL); 17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert selector = pluralProvider; 17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ordinalProvider == null) { 17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ordinalProvider = new PluralSelectorProvider(this, PluralType.ORDINAL); 17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert selector = ordinalProvider; 17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Number number = (Number)arg; 17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double offset=msgPattern.getPluralOffset(i); 17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PluralSelectorContext context = 17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new PluralSelectorContext(i, argName, number, offset); 17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int subMsgStart=PluralFormat.findSubMessage( 17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern, i, selector, context, number.doubleValue()); 17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatComplexSubMessage(subMsgStart, context, args, argsMap, dest); 17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if(argType==ArgType.SELECT) { 17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int subMsgStart=SelectFormat.findSubMessage(msgPattern, i, arg.toString()); 17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatComplexSubMessage(subMsgStart, null, args, argsMap, dest); 17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This should never happen. 17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalStateException("unexpected argType "+argType); 17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fp = updateMetaData(dest, prevDestLength, fp, argId); 17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex=msgPattern.getPart(argLimit).getLimit(); 17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert i=argLimit; 17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 17637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void formatComplexSubMessage( 17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int msgStart, PluralSelectorContext pluralNumber, 17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Object[] args, Map<String, Object> argsMap, 17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AppendableWrapper dest) { 17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!msgPattern.jdkAposMode()) { 17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(msgStart, pluralNumber, args, argsMap, dest, null); 17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return; 17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // JDK compatibility mode: (see JDK MessageFormat.format() API docs) 17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // - remove SKIP_SYNTAX; that is, remove half of the apostrophes 17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // - if the result string contains an open curly brace '{' then 17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // instantiate a temporary MessageFormat object and format again; 17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // otherwise just append the result string 17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msgString = msgPattern.getPatternString(); 17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String subMsgString; 17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder sb = null; 17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevIndex = msgPattern.getPart(msgStart).getLimit(); 17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = msgStart;;) { 17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part = msgPattern.getPart(++i); 17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type = part.getType(); 17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index = part.getIndex(); 17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type == Part.Type.MSG_LIMIT) { 17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sb == null) { 17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subMsgString = msgString.substring(prevIndex, index); 17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subMsgString = sb.append(msgString, prevIndex, index).toString(); 17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (type == Part.Type.REPLACE_NUMBER || type == Part.Type.SKIP_SYNTAX) { 17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sb == null) { 17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb = new StringBuilder(); 17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(msgString, prevIndex, index); 17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (type == Part.Type.REPLACE_NUMBER) { 17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(pluralNumber.forReplaceNumber) { 17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // number-offset was already formatted. 17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(pluralNumber.numberString); 18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(getStockNumberFormatter().format(pluralNumber.number)); 18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex = part.getLimit(); 18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (type == Part.Type.ARG_START) { 18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (sb == null) { 18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb = new StringBuilder(); 18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert sb.append(msgString, prevIndex, index); 18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex = index; 18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert i = msgPattern.getLimitPartIndex(i); 18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert index = msgPattern.getPart(i).getLimit(); 18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern.appendReducedApostrophes(msgString, prevIndex, index, sb); 18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex = index; 18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (subMsgString.indexOf('{') >= 0) { 18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessageFormat subMsgFormat = new MessageFormat("", ulocale); 18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subMsgFormat.applyPattern(subMsgString, MessagePattern.ApostropheMode.DOUBLE_REQUIRED); 18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert subMsgFormat.format(0, null, args, argsMap, dest, null); 18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.append(subMsgString); 18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Read as much literal string from the pattern string as possible. This stops 18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * as soon as it finds an argument, or it reaches the end of the string. 18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param from Index in the pattern string to start from. 18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return A substring from the pattern string representing the longest possible 18312d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * substring with no arguments. 18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String getLiteralStringUntilNextArgument(int from) { 18347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder b = new StringBuilder(); 18357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msgString=msgPattern.getPatternString(); 18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevIndex=msgPattern.getPart(from).getLimit(); 18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(int i=from+1;; ++i) { 18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part=msgPattern.getPart(i); 18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type=part.getType(); 18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index=part.getIndex(); 18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert b.append(msgString, prevIndex, index); 18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.ARG_START || type==Part.Type.MSG_LIMIT) { 18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return b.toString(); 18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR : 18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "Unexpected Part "+part+" in parsed message."; 18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex=part.getLimit(); 18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private FieldPosition updateMetaData(AppendableWrapper dest, int prevLength, 18527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FieldPosition fp, Object argId) { 18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (dest.attributes != null && prevLength < dest.length) { 18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert dest.attributes.add(new AttributeAndPosition(argId, prevLength, dest.length)); 18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fp != null && Field.ARGUMENT.equals(fp.getFieldAttribute())) { 18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fp.setBeginIndex(prevLength); 18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fp.setEndIndex(dest.length); 18597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return fp; 18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This lives here because ICU4J does not have its own ChoiceFormat class. 18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Finds the ChoiceFormat sub-message for the given number. 18677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern A MessagePattern. 18687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param partIndex the index of the first ChoiceFormat argument style part. 18697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param number a number to be mapped to one of the ChoiceFormat argument's intervals 18707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the sub-message start part index. 18717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 18727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static int findChoiceSubMessage(MessagePattern pattern, int partIndex, double number) { 18737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int count=pattern.countParts(); 18747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int msgStart; 18757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples 18767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // until ARG_LIMIT or end of choice-only pattern. 18777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Ignore the first number and selector and start the loop on the first message. 18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex+=2; 18797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(;;) { 18807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Skip but remember the current sub-message. 18817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgStart=partIndex; 18827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex=pattern.getLimitPartIndex(partIndex); 18837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(++partIndex>=count) { 18847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Reached the end of the choice-only pattern. 18857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Return with the last sub-message. 18867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 18877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part=pattern.getPart(partIndex++); 18897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type=part.getType(); 18907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.ARG_LIMIT) { 18917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Reached the end of the ChoiceFormat style. 18927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Return with the last sub-message. 18937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 18947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 18957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // part is an ARG_INT or ARG_DOUBLE 18967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert type.hasNumericValue(); 18977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double boundary=pattern.getNumericValue(part); 18987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Fetch the ARG_SELECTOR character. 18997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int selectorIndex=pattern.getPatternIndex(partIndex++); 19007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert char boundaryChar=pattern.getPatternString().charAt(selectorIndex); 19017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(boundaryChar=='<' ? !(number>boundary) : !(number>=boundary)) { 19027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The number is in the interval between the previous boundary and the current one. 19037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Return with the sub-message between them. 19047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The !(a>b) and !(a>=b) comparisons are equivalent to 19057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // (a<=b) and (a<b) except they "catch" NaN. 19067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 19077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return msgStart; 19107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 19127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Ported from C++ ChoiceFormat::parse(). 19137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static double parseChoiceArgument( 19147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern pattern, int partIndex, 19157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String source, ParsePosition pos) { 19167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // find the best number (defined as the one with the longest parse) 19177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int start = pos.getIndex(); 19187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int furthest = start; 19197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double bestNumber = Double.NaN; 19207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double tempNumber = 0.0; 19217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (pattern.getPartType(partIndex) != Part.Type.ARG_LIMIT) { 19227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert tempNumber = pattern.getNumericValue(pattern.getPart(partIndex)); 19237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex += 2; // skip the numeric part and ignore the ARG_SELECTOR 19247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int msgLimit = pattern.getLimitPartIndex(partIndex); 19257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int len = matchStringUntilLimitPart(pattern, partIndex, msgLimit, source, start); 19267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (len >= 0) { 19277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int newIndex = start + len; 19287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (newIndex > furthest) { 19297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert furthest = newIndex; 19307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert bestNumber = tempNumber; 19317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (furthest == source.length()) { 19327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 19337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex = msgLimit + 1; 19377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (furthest == start) { 19397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setErrorIndex(start); 19407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 19417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert pos.setIndex(furthest); 19427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return bestNumber; 19447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 19467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 19477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Matches the pattern string from the end of the partIndex to 19487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the beginning of the limitPartIndex, 19497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * including all syntax except SKIP_SYNTAX, 19507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * against the source string starting at sourceOffset. 19517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If they match, returns the length of the source string match. 19527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Otherwise returns -1. 19537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 19547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static int matchStringUntilLimitPart( 19557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern pattern, int partIndex, int limitPartIndex, 19567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String source, int sourceOffset) { 19577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int matchingSourceLength = 0; 19587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msgString = pattern.getPatternString(); 19597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevIndex = pattern.getPart(partIndex).getLimit(); 19607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (;;) { 19617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part = pattern.getPart(++partIndex); 19627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (partIndex == limitPartIndex || part.getType() == Part.Type.SKIP_SYNTAX) { 19637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index = part.getIndex(); 19647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int length = index - prevIndex; 19657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (length != 0 && !source.regionMatches(sourceOffset, msgString, prevIndex, length)) { 19667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; // mismatch 19677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert matchingSourceLength += length; 19697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (partIndex == limitPartIndex) { 19707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return matchingSourceLength; 19717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert prevIndex = part.getLimit(); // SKIP_SYNTAX 19737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 19777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 19787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Finds the "other" sub-message. 19797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param partIndex the index of the first PluralFormat argument style part. 19807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the "other" sub-message start part index. 19817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 19827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int findOtherSubMessage(int partIndex) { 19837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int count=msgPattern.countParts(); 19847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern.Part part=msgPattern.getPart(partIndex); 19857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(part.getType().hasNumericValue()) { 19867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++partIndex; 19877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples 19897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // until ARG_LIMIT or end of plural-only pattern. 19907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert do { 19917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert part=msgPattern.getPart(partIndex++); 19927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern.Part.Type type=part.getType(); 19937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==MessagePattern.Part.Type.ARG_LIMIT) { 19947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 19957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 19967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert type==MessagePattern.Part.Type.ARG_SELECTOR; 19977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // part is an ARG_SELECTOR followed by an optional explicit value, and then a message 19987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(msgPattern.partSubstringMatches(part, "other")) { 19997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return partIndex; 20007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(msgPattern.getPartType(partIndex).hasNumericValue()) { 20027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++partIndex; // skip the numeric-value part of "=1" etc. 20037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert partIndex=msgPattern.getLimitPartIndex(partIndex); 20057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } while(++partIndex<count); 20067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return 0; 20077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 20097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 20107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the ARG_START index of the first occurrence of the plural number in a sub-message. 20117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns -1 if it is a REPLACE_NUMBER. 20127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns 0 if there is neither. 20137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 20147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int findFirstPluralNumberArg(int msgStart, String argName) { 20157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(int i=msgStart+1;; ++i) { 20167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part=msgPattern.getPart(i); 20177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part.Type type=part.getType(); 20187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.MSG_LIMIT) { 20197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return 0; 20207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.REPLACE_NUMBER) { 20227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; 20237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(type==Part.Type.ARG_START) { 20257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArgType argType=part.getArgType(); 20267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(argName.length()!=0 && (argType==ArgType.NONE || argType==ArgType.SIMPLE)) { 20277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert part=msgPattern.getPart(i+1); // ARG_NUMBER or ARG_NAME 20287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(msgPattern.partSubstringMatches(part, argName)) { 20297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return i; 20307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert i=msgPattern.getLimitPartIndex(i); 20337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 20377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 20387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Mutable input/output values for the PluralSelectorProvider. 20397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Separate so that it is possible to make MessageFormat Freezable. 20407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 20417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class PluralSelectorContext { 20427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private PluralSelectorContext(int start, String name, Number num, double off) { 20437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert startIndex = start; 20447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert argName = name; 20457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // number needs to be set even when select() is not called. 20467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Keep it as a Number/Formattable: 20477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // For format() methods, and to preserve information (e.g., BigDecimal). 20487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(off == 0) { 20497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert number = num; 20507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 20517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert number = num.doubleValue() - off; 20527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offset = off; 20547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 20567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String toString() { 20577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new AssertionError("PluralSelectorContext being formatted, rather than its number"); 20587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 20607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Input values for plural selection with decimals. 20617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int startIndex; 20627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String argName; 20637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** argument number - plural offset */ 20647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Number number; 20657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert double offset; 20667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Output values for plural selection with decimals. 20677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */ 20687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int numberArgIndex; 20697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format formatter; 20707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** formatted argument number - plural offset */ 20717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String numberString; 20727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** true if number-offset was formatted with the stock number formatter */ 20737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert boolean forReplaceNumber; 20747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 20767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 20777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This provider helps defer instantiation of a PluralRules object 20787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * until we actually need to select a keyword. 20797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if the number matches an explicit-value selector like "=1" 20807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * we do not need any PluralRules. 20817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 20827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class PluralSelectorProvider implements PluralFormat.PluralSelector { 20837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public PluralSelectorProvider(MessageFormat mf, PluralType type) { 20847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgFormat = mf; 20857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.type = type; 20867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 20887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String select(Object ctx, double number) { 20897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(rules == null) { 20907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert rules = PluralRules.forLocale(msgFormat.ulocale, type); 20917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 20927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Select a sub-message according to how the number is formatted, 20937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // which is specified in the selected sub-message. 20947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // We avoid this circle by looking at how 20957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // the number is formatted in the "other" sub-message 20967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // which must always be present and usually contains the number. 20977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Message authors should be consistent across sub-messages. 20987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PluralSelectorContext context = (PluralSelectorContext)ctx; 20997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int otherIndex = msgFormat.findOtherSubMessage(context.startIndex); 21007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName); 21017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != null) { 21027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert context.formatter = msgFormat.cachedFormatters.get(context.numberArgIndex); 21037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(context.formatter == null) { 21057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert context.formatter = msgFormat.getStockNumberFormatter(); 21067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert context.forReplaceNumber = true; 21077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assert context.number.doubleValue() == number; // argument number minus the offset 21097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert context.numberString = context.formatter.format(context.number); 21107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(context.formatter instanceof DecimalFormat) { 21117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FixedDecimal dec = ((DecimalFormat)context.formatter).getFixedDecimal(number); 21127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return rules.select(dec); 21137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 21147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return rules.select(number); 21157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private MessageFormat msgFormat; 21187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private PluralRules rules; 21197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private PluralType type; 21207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @SuppressWarnings("unchecked") 21237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void format(Object arguments, AppendableWrapper result, FieldPosition fp) { 21247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ((arguments == null || arguments instanceof Map)) { 21257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(null, (Map<String, Object>)arguments, result, fp); 21267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 21277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format((Object[])arguments, null, result, fp); 21287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 21327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Internal routine used by format. 21337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 21347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws IllegalArgumentException if an argument in the 21357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>arguments</code> map is not of the type 21367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * expected by the format element(s) that use it. 21377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 21387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void format(Object[] arguments, Map<String, Object> argsMap, 21397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AppendableWrapper dest, FieldPosition fp) { 21407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (arguments != null && msgPattern.hasNamedArguments()) { 21417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException( 21427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "This method is not available in MessageFormat objects " + 21437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "that use alphanumeric argument names."); 21447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert format(0, null, arguments, argsMap, dest, fp); 21467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void resetPattern() { 21497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern != null) { 21507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern.clear(); 21517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (cachedFormatters != null) { 21537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert cachedFormatters.clear(); 21547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert customFormatArgStarts = null; 21567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 21577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final String[] typeList = 21597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { "number", "date", "time", "spellout", "ordinal", "duration" }; 21607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int 21617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_NUMBER = 0, 21627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_DATE = 1, 21637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_TIME = 2, 21647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_SPELLOUT = 3, 21657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_ORDINAL = 4, 21667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TYPE_DURATION = 5; 21677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final String[] modifierList = 21697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert {"", "currency", "percent", "integer"}; 21707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int 21727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MODIFIER_EMPTY = 0, 21737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MODIFIER_CURRENCY = 1, 21747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MODIFIER_PERCENT = 2, 21757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MODIFIER_INTEGER = 3; 21767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final String[] dateModifierList = 21787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert {"", "short", "medium", "long", "full"}; 21797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int 21817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DATE_MODIFIER_EMPTY = 0, 21827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DATE_MODIFIER_SHORT = 1, 21837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DATE_MODIFIER_MEDIUM = 2, 21847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DATE_MODIFIER_LONG = 3, 21857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DATE_MODIFIER_FULL = 4; 21867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 21877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Creates an appropriate Format object for the type and style passed. 21887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Both arguments cannot be null. 21897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Format createAppropriateFormat(String type, String style) { 21907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format newFormat = null; 21917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int subformatType = findKeyword(type, typeList); 21927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (subformatType){ 21937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_NUMBER: 21947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (findKeyword(style, modifierList)) { 21957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case MODIFIER_EMPTY: 21967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = NumberFormat.getInstance(ulocale); 21977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 21987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case MODIFIER_CURRENCY: 21997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = NumberFormat.getCurrencyInstance(ulocale); 22007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case MODIFIER_PERCENT: 22027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = NumberFormat.getPercentInstance(ulocale); 22037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case MODIFIER_INTEGER: 22057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = NumberFormat.getIntegerInstance(ulocale); 22067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: // pattern 22087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = new DecimalFormat(style, 22097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new DecimalFormatSymbols(ulocale)); 22107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_DATE: 22147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (findKeyword(style, dateModifierList)) { 22157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_EMPTY: 22167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale); 22177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_SHORT: 22197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getDateInstance(DateFormat.SHORT, ulocale); 22207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_MEDIUM: 22227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, ulocale); 22237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_LONG: 22257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getDateInstance(DateFormat.LONG, ulocale); 22267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_FULL: 22287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getDateInstance(DateFormat.FULL, ulocale); 22297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 22317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = new SimpleDateFormat(style, ulocale); 22327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_TIME: 22367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (findKeyword(style, dateModifierList)) { 22377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_EMPTY: 22387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale); 22397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_SHORT: 22417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, ulocale); 22427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_MEDIUM: 22447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, ulocale); 22457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_LONG: 22477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getTimeInstance(DateFormat.LONG, ulocale); 22487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case DATE_MODIFIER_FULL: 22507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = DateFormat.getTimeInstance(DateFormat.FULL, ulocale); 22517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 22537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = new SimpleDateFormat(style, ulocale); 22547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_SPELLOUT: 22587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 22597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 22607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat.SPELLOUT); 22617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String ruleset = style.trim(); 22627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ruleset.length() != 0) { 22637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 22647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert rbnf.setDefaultRuleSet(ruleset); 22657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert catch (Exception e) { 22677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // warn invalid ruleset 22687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = rbnf; 22717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_ORDINAL: 22747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 22757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 22767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat.ORDINAL); 22777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String ruleset = style.trim(); 22787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ruleset.length() != 0) { 22797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 22807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert rbnf.setDefaultRuleSet(ruleset); 22817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert catch (Exception e) { 22837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // warn invalid ruleset 22847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = rbnf; 22877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 22897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case TYPE_DURATION: 22907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert { 22917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 22927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert RuleBasedNumberFormat.DURATION); 22937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String ruleset = style.trim(); 22947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ruleset.length() != 0) { 22957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 22967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert rbnf.setDefaultRuleSet(ruleset); 22977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 22987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert catch (Exception e) { 22997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // warn invalid ruleset 23007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newFormat = rbnf; 23037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 23057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 23067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("Unknown format type \"" + type + "\""); 23077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return newFormat; 23097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 23117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final Locale rootLocale = new Locale(""); // Locale.ROOT only @since 1.6 23127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 23137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int findKeyword(String s, String[] list) { 23147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert s = PatternProps.trimWhiteSpace(s).toLowerCase(rootLocale); 23157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < list.length; ++i) { 23167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (s.equals(list[i])) 23177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return i; 23187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return -1; 23207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 23227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 23237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Custom serialization, new in ICU 4.8. 23247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We do not want to use default serialization because we only have a small 23257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * amount of persistent state which is better expressed explicitly 23267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * rather than via writing field objects. 23277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param out The output stream. 23287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @serialData Writes the locale as a BCP 47 language tag string, 23297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the MessagePattern.ApostropheMode as an object, 23307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the pattern string (null if none was applied). 23317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Followed by an int with the number of (int formatIndex, Object formatter) pairs, 23327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and that many such pairs, corresponding to previous setFormat() calls for custom formats. 23337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Followed by an int with the number of (int, Object) pairs, 23347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and that many such pairs, for future (post-ICU 4.8) extension of the serialization format. 23357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 23367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void writeObject(java.io.ObjectOutputStream out) throws IOException { 23377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.defaultWriteObject(); 23387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ICU 4.8 custom serialization. 23397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // locale as a BCP 47 language tag 23407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeObject(ulocale.toLanguageTag()); 23417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ApostropheMode 23427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null) { 23437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern = new MessagePattern(); 23447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeObject(msgPattern.getApostropheMode()); 23467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // message pattern string 23477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeObject(msgPattern.getPatternString()); 23487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // custom formatters 23497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (customFormatArgStarts == null || customFormatArgStarts.isEmpty()) { 23507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeInt(0); 23517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 23527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeInt(customFormatArgStarts.size()); 23537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int formatIndex = 0; 23547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 23557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (customFormatArgStarts.contains(partIndex)) { 23567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeInt(formatIndex); 23577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeObject(cachedFormatters.get(partIndex)); 23587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++formatIndex; 23607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // number of future (int, Object) pairs 23637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out.writeInt(0); 23647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 23667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 23677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Custom deserialization, new in ICU 4.8. See comments on writeObject(). 23687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @throws InvalidObjectException if the objects read from the stream is invalid. 23697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 23707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 23717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert in.defaultReadObject(); 23727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ICU 4.8 custom deserialization. 23737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String languageTag = (String)in.readObject(); 23747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ulocale = ULocale.forLanguageTag(languageTag); 23757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert MessagePattern.ApostropheMode aposMode = (MessagePattern.ApostropheMode)in.readObject(); 23767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msgPattern == null || aposMode != msgPattern.getApostropheMode()) { 23777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert msgPattern = new MessagePattern(aposMode); 23787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String msg = (String)in.readObject(); 23807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (msg != null) { 23817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert applyPattern(msg); 23827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // custom formatters 23847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int numFormatters = in.readInt(); numFormatters > 0; --numFormatters) { 23857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int formatIndex = in.readInt(); 23867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format formatter = (Format)in.readObject(); 23877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setFormat(formatIndex, formatter); 23887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // skip future (int, Object) pairs 23907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int numPairs = in.readInt(); numPairs > 0; --numPairs) { 23917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert in.readInt(); 23927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert in.readObject(); 23937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 23957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 23967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void cacheExplicitFormats() { 23977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (cachedFormatters != null) { 23987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert cachedFormatters.clear(); 23997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert customFormatArgStarts = null; 24017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT 24027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // which we need not examine. 24037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int limit = msgPattern.countParts() - 2; 24047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // This loop starts at part index 1 because we do need to examine 24057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // ARG_START parts. (But we can ignore the MSG_START.) 24067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for(int i=1; i < limit; ++i) { 24077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Part part = msgPattern.getPart(i); 24087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(part.getType()!=Part.Type.ARG_START) { 24097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 24107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ArgType argType=part.getArgType(); 24127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(argType != ArgType.SIMPLE) { 24137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert continue; 24147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int index = i; 24167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert i += 2; 24177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String explicitType = msgPattern.getSubstring(msgPattern.getPart(i++)); 24187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String style = ""; 24197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ((part = msgPattern.getPart(i)).getType() == MessagePattern.Part.Type.ARG_STYLE) { 24207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert style = msgPattern.getSubstring(part); 24217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++i; 24227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Format formatter = createAppropriateFormat(explicitType, style); 24247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setArgStartFormat(index, formatter); 24257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 24287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 24297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets a formatter for a MessagePattern ARG_START part index. 24307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 24317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void setArgStartFormat(int argStart, Format formatter) { 24327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (cachedFormatters == null) { 24337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert cachedFormatters = new HashMap<Integer, Format>(); 24347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert cachedFormatters.put(argStart, formatter); 24367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 24387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 24397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Sets a custom formatter for a MessagePattern ARG_START part index. 24407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "Custom" formatters are provided by the user via setFormat() or similar APIs. 24417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 24427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void setCustomArgStartFormat(int argStart, Format formatter) { 24437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setArgStartFormat(argStart, formatter); 24447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (customFormatArgStarts == null) { 24457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert customFormatArgStarts = new HashSet<Integer>(); 24467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert customFormatArgStarts.add(argStart); 24487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 24497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 24507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final char SINGLE_QUOTE = '\''; 24517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final char CURLY_BRACE_LEFT = '{'; 24527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final char CURLY_BRACE_RIGHT = '}'; 24537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 24547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int STATE_INITIAL = 0; 24557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int STATE_SINGLE_QUOTE = 1; 24567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int STATE_IN_QUOTE = 2; 24577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final int STATE_MSG_ELEMENT = 3; 24587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 24597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 24607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * {@icu} Converts an 'apostrophe-friendly' pattern into a standard 24617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern. 24627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <em>This is obsolete for ICU 4.8 and higher MessageFormat pattern strings.</em> 246303f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka * It can still be useful together with {@link java.text.MessageFormat}. 24647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 24657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>See the class description for more about apostrophes and quoting, 246603f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka * and differences between ICU and {@link java.text.MessageFormat}. 24677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 246803f16b04d95bbaa98f702b69791b0de29ac75915Yoshito Umaoka * <p>{@link java.text.MessageFormat} and ICU 4.6 and earlier MessageFormat 24697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * treat all ASCII apostrophes as 24707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * quotes, which is problematic in some languages, e.g. 24717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * French, where apostrophe is commonly used. This utility 24727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * assumes that only an unpaired apostrophe immediately before 24737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * a brace is a true quote. Other unpaired apostrophes are paired, 24747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the resulting standard pattern string is returned. 24757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 24767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p><b>Note</b>: It is not guaranteed that the returned pattern 24777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is indeed a valid pattern. The only effect is to convert 24787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * between patterns having different quoting semantics. 24797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 24807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p><b>Note</b>: This method only works on top-level messageText, 24817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * not messageText nested inside a complexArg. 24827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 24837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern the 'apostrophe-friendly' pattern to convert 24847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the standard equivalent of the original pattern 24857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 3.4 24867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 24877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static String autoQuoteApostrophe(String pattern) { 24887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder buf = new StringBuilder(pattern.length() * 2); 24897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int state = STATE_INITIAL; 24907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int braceCount = 0; 24917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0, j = pattern.length(); i < j; ++i) { 24927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert char c = pattern.charAt(i); 24937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (state) { 24947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case STATE_INITIAL: 24957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (c) { 24967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case SINGLE_QUOTE: 24977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_SINGLE_QUOTE; 24987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 24997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case CURLY_BRACE_LEFT: 25007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_MSG_ELEMENT; 25017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++braceCount; 25027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case STATE_SINGLE_QUOTE: 25067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (c) { 25077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case SINGLE_QUOTE: 25087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_INITIAL; 25097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case CURLY_BRACE_LEFT: 25117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case CURLY_BRACE_RIGHT: 25127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_IN_QUOTE; 25137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 25157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert buf.append(SINGLE_QUOTE); 25167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_INITIAL; 25177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case STATE_IN_QUOTE: 25217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (c) { 25227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case SINGLE_QUOTE: 25237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_INITIAL; 25247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case STATE_MSG_ELEMENT: 25287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (c) { 25297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case CURLY_BRACE_LEFT: 25307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ++braceCount; 25317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case CURLY_BRACE_RIGHT: 25337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (--braceCount == 0) { 25347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = STATE_INITIAL; 25357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ///CLOVER:OFF 25407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: // Never happens. 25417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 25427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ///CLOVER:ON 25437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert buf.append(c); 25457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // End of scan 25477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) { 25487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert buf.append(SINGLE_QUOTE); 25497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new String(buf); 25517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 25547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Convenience wrapper for Appendable, tracks the result string length. 25557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Also, Appendable throws IOException, and we turn that into a RuntimeException 25567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * so that we need no throws clauses. 25577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 25587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class AppendableWrapper { 25597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public AppendableWrapper(StringBuilder sb) { 25607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert app = sb; 25617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert length = sb.length(); 25627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert attributes = null; 25637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public AppendableWrapper(StringBuffer sb) { 25667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert app = sb; 25677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert length = sb.length(); 25687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert attributes = null; 25697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void useAttributes() { 25727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert attributes = new ArrayList<AttributeAndPosition>(); 25737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void append(CharSequence s) { 25767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 25777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert app.append(s); 25787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert length += s.length(); 25797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(IOException e) { 25807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ICUUncheckedIOException(e); 25817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void append(CharSequence s, int start, int limit) { 25857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 25867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert app.append(s, start, limit); 25877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert length += limit - start; 25887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(IOException e) { 25897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ICUUncheckedIOException(e); 25907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void append(CharacterIterator iterator) { 25947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert length += append(app, iterator); 25957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 25967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 25977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static int append(Appendable result, CharacterIterator iterator) { 25987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 25997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int start = iterator.getBeginIndex(); 26007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int limit = iterator.getEndIndex(); 26017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int length = limit - start; 26027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (start < limit) { 26037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.append(iterator.first()); 26047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (++start < limit) { 26057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result.append(iterator.next()); 26067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return length; 26097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(IOException e) { 26107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new ICUUncheckedIOException(e); 26117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void formatAndAppend(Format formatter, Object arg) { 26157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (attributes == null) { 26167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert append(formatter.format(arg)); 26177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 26187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert AttributedCharacterIterator formattedArg = formatter.formatToCharacterIterator(arg); 26197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int prevLength = length; 26207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert append(formattedArg); 26217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Copy all of the attributes from formattedArg to our attributes list. 26227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formattedArg.first(); 26237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int start = formattedArg.getIndex(); // Should be 0 but might not be. 26247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int limit = formattedArg.getEndIndex(); // == start + length - prevLength 26257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int offset = prevLength - start; // Adjust attribute indexes for the result string. 26267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert while (start < limit) { 26277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Map<Attribute, Object> map = formattedArg.getAttributes(); 26287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int runLimit = formattedArg.getRunLimit(); 26297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (map.size() != 0) { 26307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (Map.Entry<Attribute, Object> entry : map.entrySet()) { 26317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert attributes.add( 26327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new AttributeAndPosition( 26337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert entry.getKey(), entry.getValue(), 26347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offset + start, offset + runLimit)); 26357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert start = runLimit; 26387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formattedArg.setIndex(start); 26397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void formatAndAppend(Format formatter, Object arg, String argString) { 26447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (attributes == null && argString != null) { 26457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert append(argString); 26467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 26477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatAndAppend(formatter, arg); 26487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Appendable app; 26527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int length; 26537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private List<AttributeAndPosition> attributes; 26547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final class AttributeAndPosition { 26577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 26587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Defaults the field to Field.ARGUMENT. 26597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 26607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public AttributeAndPosition(Object fieldValue, int startIndex, int limitIndex) { 26617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert init(Field.ARGUMENT, fieldValue, startIndex, limitIndex); 26627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public AttributeAndPosition(Attribute field, Object fieldValue, int startIndex, int limitIndex) { 26657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert init(field, fieldValue, startIndex, limitIndex); 26667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void init(Attribute field, Object fieldValue, int startIndex, int limitIndex) { 26697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert key = field; 26707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert value = fieldValue; 26717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert start = startIndex; 26727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert limit = limitIndex; 26737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 26757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Attribute key; 26767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Object value; 26777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int start; 26787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int limit; 26797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 26807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 2681