15d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath/* 25d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath********************************************************************** 38d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath* Copyright (c) 2004-2014, International Business Machines 45d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* Corporation and others. All Rights Reserved. 55d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath********************************************************************** 65d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* Author: Alan Liu 75d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* Created: April 6, 2004 85d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* Since: ICU 3.0 95d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath********************************************************************** 105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath*/ 118d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathpackage com.ibm.icu.simple; 125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.io.IOException; 145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.io.InvalidObjectException; 155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.AttributedCharacterIterator; 165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.AttributedCharacterIterator.Attribute; 175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.AttributedString; 185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.CharacterIterator; 195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.ChoiceFormat; 208d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport java.text.DateFormat; 218d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport java.text.DecimalFormat; 228d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport java.text.DecimalFormatSymbols; 235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.FieldPosition; 245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.Format; 258d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport java.text.NumberFormat; 265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.ParseException; 275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.text.ParsePosition; 288d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport java.text.SimpleDateFormat; 295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.ArrayList; 305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.Date; 315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.HashMap; 325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.HashSet; 335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.List; 345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.Locale; 355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.Map; 365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.Set; 375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.impl.PatternProps; 398d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport com.ibm.icu.simple.PluralRules.PluralType; 408d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport com.ibm.icu.text.MessagePattern; 415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.text.MessagePattern.ArgType; 425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.text.MessagePattern.Part; 438d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport com.ibm.icu.text.SelectFormat; 448d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport com.ibm.icu.util.ICUUncheckedIOException; 455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath/** 475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icuenhanced java.text.MessageFormat}.{@icu _usage_} 485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>MessageFormat prepares strings for display to users, 505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with optional arguments (variables/placeholders). 515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The arguments can occur in any order, which is necessary for translation 525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * into languages with different grammars. 535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>A MessageFormat is constructed from a <em>pattern</em> string 555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with arguments in {curly braces} which will be replaced by formatted values. 565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p><code>MessageFormat</code> differs from the other <code>Format</code> 585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * classes in that you create a <code>MessageFormat</code> object with one 595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * of its constructors (not with a <code>getInstance</code> style factory 605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * method). Factory methods aren't necessary because <code>MessageFormat</code> 615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * itself doesn't implement locale-specific behavior. Any locale-specific 625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * behavior is defined by the pattern that you provide and the 635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * subformats used for inserted arguments. 645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Arguments can be named (using identifiers) or numbered (using small ASCII-digit integers). 665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Some of the API methods work only with argument numbers and throw an exception 675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * if the pattern has named arguments (see {@link #usesNamedArguments()}). 685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>An argument might not specify any format type. In this case, 705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a Number value is formatted with a default (for the locale) NumberFormat, 715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a Date value is formatted with a default (for the locale) DateFormat, 725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and for any other value its toString() value is used. 735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>An argument might specify a "simple" type for which the specified 755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Format object is created, cached and used. 765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>An argument might have a "complex" type with nested MessageFormat sub-patterns. 785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * During formatting, one of these sub-messages is selected according to the argument value 795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and recursively formatted. 805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>After construction, a custom Format object can be set for 825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a top-level argument, overriding the default formatting and parsing behavior 835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for that argument. 845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * However, custom formatting can be achieved more simply by writing 855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a typeless argument in the pattern string 865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and supplying it with a preformatted string value. 875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>When formatting, MessageFormat takes a collection of argument values 895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and writes an output string. 905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument values may be passed as an array 915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (when the pattern contains only numbered arguments) 925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * or as a Map (which works for both named and numbered arguments). 935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Each argument is matched with one of the input values by array index or map key 955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and formatted according to its pattern specification 965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (or using a custom Format object if one was set). 975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A numbered pattern argument is matched with a map key that contains that number 985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as an ASCII-decimal-digit string (without leading zero). 995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <h4><a name="patterns">Patterns and Their Interpretation</a></h4> 1015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>MessageFormat</code> uses patterns of the following form: 1035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote><pre> 1045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * message = messageText (argument messageText)* 1055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument = noneArg | simpleArg | complexArg 1065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * complexArg = choiceArg | pluralArg | selectArg | selectordinalArg 1075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * noneArg = '{' argNameOrNumber '}' 1095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * simpleArg = '{' argNameOrNumber ',' argType [',' argStyle] '}' 1105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * choiceArg = '{' argNameOrNumber ',' "choice" ',' choiceStyle '}' 1115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pluralArg = '{' argNameOrNumber ',' "plural" ',' pluralStyle '}' 1125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * selectArg = '{' argNameOrNumber ',' "select" ',' selectStyle '}' 1135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * selectordinalArg = '{' argNameOrNumber ',' "selectordinal" ',' pluralStyle '}' 1145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * choiceStyle: see {@link ChoiceFormat} 1165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pluralStyle: see {@link PluralFormat} 1175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * selectStyle: see {@link SelectFormat} 1185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argNameOrNumber = argName | argNumber 1205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argName = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+ 1215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argNumber = '0' | ('1'..'9' ('0'..'9')*) 1225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argType = "number" | "date" | "time" | "spellout" | "ordinal" | "duration" 1245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argStyle = "short" | "medium" | "long" | "full" | "integer" | "currency" | "percent" | argStyleText 1255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </pre></blockquote> 1265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <ul> 1285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>messageText can contain quoted literal strings including syntax characters. 1295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A quoted literal string begins with an ASCII apostrophe and a syntax character 1305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (usually a {curly brace}) and continues until the next single apostrophe. 1315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A double ASCII apostrohpe inside or outside of a quoted string represents 1325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * one literal apostrophe. 1335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>Quotable syntax characters are the {curly braces} in all messageText parts, 1345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * plus the '#' sign in a messageText immediately inside a pluralStyle, 1355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and the '|' symbol in a messageText immediately inside a choiceStyle. 1365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>See also {@link MessagePattern.ApostropheMode} 1375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>In argStyleText, every single ASCII apostrophe begins and ends quoted literal text, 1385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and unquoted {curly braces} must occur in matched pairs. 1395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </ul> 1405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Recommendation: Use the real apostrophe (single quote) character \u2019 for 1425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * human-readable text, and use the ASCII apostrophe (\u0027 ' ) 1435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * only in program syntax, like quoting in MessageFormat. 1445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * See the annotations for U+0027 Apostrophe in The Unicode Standard. 1455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>The <code>choice</code> argument type is deprecated. 1475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Use <code>plural</code> arguments for proper plural selection, 1485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and <code>select</code> arguments for simple selection among a fixed set of choices. 1495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>The <code>argType</code> and <code>argStyle</code> values are used to create 1515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a <code>Format</code> instance for the format element. The following 1525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * table shows how the values map to Format instances. Combinations not 1535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * shown in the table are illegal. Any <code>argStyleText</code> must 1545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * be a valid pattern string for the Format subclass used. 1555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 1565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p><table border=1> 1575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>argType 1595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>argStyle 1605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>resulting Format object 1615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td colspan=2><i>(none)</i> 1635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>null</code> 1645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td rowspan=5><code>number</code> 1665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>(none)</i> 1675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>NumberFormat.getInstance(getLocale())</code> 1685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>integer</code> 1705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>NumberFormat.getIntegerInstance(getLocale())</code> 1715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>currency</code> 1735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>NumberFormat.getCurrencyInstance(getLocale())</code> 1745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>percent</code> 1765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>NumberFormat.getPercentInstance(getLocale())</code> 1775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText</i> 1795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new DecimalFormat(argStyleText, new DecimalFormatSymbols(getLocale()))</code> 1805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td rowspan=6><code>date</code> 1825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>(none)</i> 1835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> 1845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>short</code> 1865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateInstance(DateFormat.SHORT, getLocale())</code> 1875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>medium</code> 1895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())</code> 1905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>long</code> 1925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateInstance(DateFormat.LONG, getLocale())</code> 1935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>full</code> 1955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateInstance(DateFormat.FULL, getLocale())</code> 1965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText</i> 1985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new SimpleDateFormat(argStyleText, getLocale()) 1995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td rowspan=6><code>time</code> 2015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>(none)</i> 2025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> 2035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>short</code> 2055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())</code> 2065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>medium</code> 2085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())</code> 2095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>long</code> 2115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getTimeInstance(DateFormat.LONG, getLocale())</code> 2125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>full</code> 2145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getTimeInstance(DateFormat.FULL, getLocale())</code> 2155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText</i> 2175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new SimpleDateFormat(argStyleText, getLocale()) 2185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>spellout</code> 2205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText (optional)</i> 2215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.SPELLOUT) 2225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <br/> .setDefaultRuleset(argStyleText);</code> 2235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>ordinal</code> 2255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText (optional)</i> 2265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.ORDINAL) 2275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <br/> .setDefaultRuleset(argStyleText);</code> 2285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 2295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>duration</code> 2305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>argStyleText (optional)</i> 2315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>new RuleBasedNumberFormat(getLocale(), RuleBasedNumberFormat.DURATION) 2325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <br/> .setDefaultRuleset(argStyleText);</code> 2335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </table> 2345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 2355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <h4><a name="diffsjdk">Differences from java.text.MessageFormat</a></h4> 2375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>The ICU MessageFormat supports both named and numbered arguments, 2395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * while the JDK MessageFormat only supports numbered arguments. 2405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Named arguments make patterns more readable. 2415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>ICU implements a more user-friendly apostrophe quoting syntax. 2435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In message text, an apostrophe only begins quoting literal text 2445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * if it immediately precedes a syntax character (mostly {curly braces}).<br> 2455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In the JDK MessageFormat, an apostrophe always begins quoting, 2465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * which requires common text like "don't" and "aujourd'hui" 2475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to be written with doubled apostrophes like "don''t" and "aujourd''hui". 2485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For more details see {@link MessagePattern.ApostropheMode}. 2495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>ICU does not create a ChoiceFormat object for a choiceArg, pluralArg or selectArg 2515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * but rather handles such arguments itself. 2525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The JDK MessageFormat does create and use a ChoiceFormat object 2535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (<code>new ChoiceFormat(argStyleText)</code>). 2545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The JDK does not support plural and select arguments at all. 2555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <h4>Usage Information</h4> 2575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Here are some examples of usage: 2595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote> 2605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <pre> 2615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Object[] arguments = { 2625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7, 2635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * new Date(System.currentTimeMillis()), 2645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "a disturbance in the Force" 2655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * }; 2665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * String result = MessageFormat.format( 2685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", 2695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments); 2705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>output</em>: At 12:30 PM on Jul 3, 2053, there was a disturbance 2725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in the Force on planet 7. 2735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </pre> 2755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </blockquote> 2765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Typically, the message format will come from resources, and the 2775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments will be dynamically set at runtime. 2785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Example 2: 2805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote> 2815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <pre> 2825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Object[] testArgs = { 3, "MyDisk" }; 2835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * MessageFormat form = new MessageFormat( 2855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "The disk \"{1}\" contains {0} file(s)."); 2865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * System.out.println(form.format(testArgs)); 2885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * // output, with different testArgs 2905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>output</em>: The disk "MyDisk" contains 0 file(s). 2915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>output</em>: The disk "MyDisk" contains 1 file(s). 2925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>output</em>: The disk "MyDisk" contains 1,273 file(s). 2935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </pre> 2945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </blockquote> 2955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 2965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>For messages that include plural forms, you can use a plural argument: 2975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <pre> 2985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * MessageFormat msgFmt = new MessageFormat( 2995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "{num_files, plural, " + 3005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "=0{There are no files on disk \"{disk_name}\".}" + 3015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "=1{There is one file on disk \"{disk_name}\".}" + 3025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "other{There are # files on disk \"{disk_name}\".}}", 3035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ULocale.ENGLISH); 3045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Map args = new HashMap(); 3055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * args.put("num_files", 0); 3065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * args.put("disk_name", "MyDisk"); 3075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * System.out.println(msgFmt.format(args)); 3085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * args.put("num_files", 3); 3095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * System.out.println(msgFmt.format(args)); 3105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>output</em>: 3125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * There are no files on disk "MyDisk". 3135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * There are 3 files on "MyDisk". 3145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </pre> 3155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * See {@link PluralFormat} and {@link PluralRules} for details. 3165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <h4><a name="synchronization">Synchronization</a></h4> 3185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>MessageFormats are not synchronized. 3205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * It is recommended to create separate format instances for each thread. 3215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If multiple threads access a format concurrently, it must be synchronized 3225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * externally. 3235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see java.util.Locale 3255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see Format 3265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see NumberFormat 3275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see DecimalFormat 3285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see ChoiceFormat 3295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see PluralFormat 3305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see SelectFormat 3315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @author Mark Davis 3325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @author Markus Scherer 3335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 3345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3358d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathpublic class MessageFormat extends Format { 3365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Incremented by 1 for ICU 4.8's new format. 3385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath static final long serialVersionUID = 7136212545847378652L; 3395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3418d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * Formats a message pattern string with a variable number of name/value pair arguments. 3428d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * Creates an ICU MessageFormat for the locale and pattern, 3438d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * and formats with the arguments. 3448d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * 3458d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * @param locale Locale for number formatting and plural selection etc. 3468d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * @param msg an ICU-MessageFormat-syntax string 3478d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath * @param nameValuePairs (argument name, argument value) pairs 3488d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath */ 3498d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath public static final String formatNamedArgs(Locale locale, String msg, Object... nameValuePairs) { 3508d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath StringBuilder result = new StringBuilder(msg.length()); 3518d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath new MessageFormat(msg, locale).format(0, null, null, null, nameValuePairs, 3528d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath new AppendableWrapper(result), null); 3538d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath return result.toString(); 3548d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } 3558d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath 3568d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath /** 3575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constructs a MessageFormat for the default <code>FORMAT</code> locale and the 3585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * specified pattern. 3595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the locale and calls applyPattern(pattern). 3605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern the pattern for this message format 3625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception IllegalArgumentException if the pattern is invalid 3635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see Category#FORMAT 3645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 3655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessageFormat(String pattern) { 3678d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath locale_ = Locale.getDefault(); // Category.FORMAT 3685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath applyPattern(pattern); 3695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constructs a MessageFormat for the specified locale and 3735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern. 3745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the locale and calls applyPattern(pattern). 3755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern the pattern for this message format 3775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param locale the locale for this message format 3785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception IllegalArgumentException if the pattern is invalid 3795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 3805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessageFormat(String pattern, Locale locale) { 3828d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath locale_ = locale; 3835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath applyPattern(pattern); 3845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the locale that's used when creating or comparing subformats. 3885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 3895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the locale used when creating or comparing subformats 3905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 3915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Locale getLocale() { 3938d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath return locale_; 3945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the pattern used by this message format. 3985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses the pattern and caches Format objects for simple argument types. 3995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Patterns and their interpretation are specified in the 4005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <a href="#patterns">class description</a>. 4015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 4025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pttrn the pattern for this message format 4035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if the pattern is invalid 4045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 4055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void applyPattern(String pttrn) { 4075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 4085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern == null) { 4095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern = new MessagePattern(pttrn); 4105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 4115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern.parse(pttrn); 4125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Cache the formats that are explicitly mentioned in the message pattern. 4145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath cacheExplicitFormats(); 4155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } catch(RuntimeException e) { 4165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath resetPattern(); 4175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw e; 4185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Sets the ApostropheMode and the pattern used by this message format. 4235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses the pattern and caches Format objects for simple argument types. 4245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Patterns and their interpretation are specified in the 4255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <a href="#patterns">class description</a>. 4265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 4275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is best used only once on a given object to avoid confusion about the mode, 4285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and after constructing the object with an empty pattern string to minimize overhead. 4295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 4305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern the pattern for this message format 4315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param aposMode the new ApostropheMode 4325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if the pattern is invalid 4335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see MessagePattern.ApostropheMode 4345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void applyPattern(String pattern, MessagePattern.ApostropheMode aposMode) { 4375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern == null) { 4385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern = new MessagePattern(aposMode); 4395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (aposMode != msgPattern.getApostropheMode()) { 4405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern.clearPatternAndSetApostropheMode(aposMode); 4415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath applyPattern(pattern); 4435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} 4475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this instance's ApostropheMode. 4485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern.ApostropheMode getApostropheMode() { 4515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern == null) { 4525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern = new MessagePattern(); // Sets the default mode. 4535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msgPattern.getApostropheMode(); 4555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the applied pattern string. 4595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the pattern string 4605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalStateException after custom Format objects have been set 4615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * via setFormat() or similar APIs 4625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 4635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String toPattern() { 4655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Return the original, applied pattern string, or else "". 4665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Note: This does not take into account 4675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // - changes from setFormat() and similar methods, or 4685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // - normalization of apostrophes and arguments, for example, 4695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // whether some date/time/number formatter was created via a pattern 4705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // but is equivalent to the "medium" default format. 4715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (customFormatArgStarts != null) { 4725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalStateException( 4735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "toPattern() is not supported after custom Format objects "+ 4745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "have been set via setFormat() or similar APIs"); 4755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern == null) { 4775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ""; 4785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String originalPattern = msgPattern.getPatternString(); 4805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return originalPattern == null ? "" : originalPattern; 4815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the part index of the next ARG_START after partIndex, or -1 if there is none more. 4855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param partIndex Part index of the previous ARG_START (initially 0). 4865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int nextTopLevelArgStart(int partIndex) { 4885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (partIndex != 0) { 4895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex = msgPattern.getLimitPartIndex(partIndex); 4905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (;;) { 4925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern.Part.Type type = msgPattern.getPartType(++partIndex); 4935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (type == MessagePattern.Part.Type.ARG_START) { 4945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return partIndex; 4955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (type == MessagePattern.Part.Type.MSG_LIMIT) { 4975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return -1; 4985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean argNameMatches(int partIndex, String argName, int argNumber) { 5035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part = msgPattern.getPart(partIndex); 5045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return part.getType() == MessagePattern.Part.Type.ARG_NAME ? 5055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern.partSubstringMatches(part, argName) : 5065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath part.getValue() == argNumber; // ARG_NUMBER 5075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private String getArgName(int partIndex) { 5105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part = msgPattern.getPart(partIndex); 5115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (part.getType() == MessagePattern.Part.Type.ARG_NAME) { 5125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msgPattern.getSubstring(part); 5135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 5145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return Integer.toString(part.getValue()); 5155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the Format objects to use for the values passed into 5205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>format</code> methods or returned from <code>parse</code> 5215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * methods. The indices of elements in <code>newFormats</code> 5225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * correspond to the argument indices used in the previously set 5235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern string. 5245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The order of formats in <code>newFormats</code> thus corresponds to 5255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the order of elements in the <code>arguments</code> array passed 5265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to the <code>format</code> methods or the result array returned 5275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * by the <code>parse</code> methods. 5285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 5295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If an argument index is used for more than one format element 5305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in the pattern string, then the corresponding new format is used 5315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for all such format elements. If an argument index is not used 5325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for any format element in the pattern string, then the 5335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * corresponding new format is ignored. If fewer formats are provided 5345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * than needed, then only the formats for argument indices less 5355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * than <code>newFormats.length</code> are replaced. 5365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 5375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is only supported if the format does not use 5385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * named arguments, otherwise an IllegalArgumentException is thrown. 5395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 5405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormats the new formats to use 5415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NullPointerException if <code>newFormats</code> is null 5425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this formatter uses named arguments 5435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 5445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormatsByArgumentIndex(Format[] newFormats) { 5465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern.hasNamedArguments()) { 5475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 5485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "This method is not available in MessageFormat objects " + 5495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "that use alphanumeric argument names."); 5505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 5525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber = msgPattern.getPart(partIndex + 1).getValue(); 5535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNumber < newFormats.length) { 5545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormats[argNumber]); 5555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Sets the Format objects to use for the values passed into 5615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>format</code> methods or returned from <code>parse</code> 5625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * methods. The keys in <code>newFormats</code> are the argument 5635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * names in the previously set pattern string, and the values 5645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * are the formats. 5655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 5665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Only argument names from the pattern string are considered. 5675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Extra keys in <code>newFormats</code> that do not correspond 5685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to an argument name are ignored. Similarly, if there is no 5695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format in newFormats for an argument name, the formatter 5705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for that argument remains unchanged. 5715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 5725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This may be called on formats that do not use named arguments. 5735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In this case the map will be queried for key Strings that 5745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * represent argument indices, e.g. "0", "1", "2" etc. 5755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 5765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormats a map from String to Format providing new 5775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * formats for named arguments. 5785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 5795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormatsByArgumentName(Map<String, Format> newFormats) { 5815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 5825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String key = getArgName(partIndex + 1); 5835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (newFormats.containsKey(key)) { 5845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormats.get(key)); 5855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the Format objects to use for the format elements in the 5915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * previously set pattern string. 5925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The order of formats in <code>newFormats</code> corresponds to 5935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the order of format elements in the pattern string. 5945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 5955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If more formats are provided than needed by the pattern string, 5965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the remaining ones are ignored. If fewer formats are provided 5975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * than needed, then only the first <code>newFormats.length</code> 5985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * formats are replaced. 5995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Since the order of format elements in a pattern string often 6015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * changes during localization, it is generally better to use the 6025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex} 6035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * method, which assumes an order of formats corresponding to the 6045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * order of elements in the <code>arguments</code> array passed to 6055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the <code>format</code> methods or the result array returned by 6065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the <code>parse</code> methods. 6075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 6085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormats the new formats to use 6095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception NullPointerException if <code>newFormats</code> is null 6105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 6115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormats(Format[] newFormats) { 6135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int formatNumber = 0; 6145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; 6155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath formatNumber < newFormats.length && 6165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormats[formatNumber]); 6185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++formatNumber; 6195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 6225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the Format object to use for the format elements within the 6245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * previously set pattern string that use the given argument 6255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * index. 6265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument index is part of the format element definition and 6275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * represents an index into the <code>arguments</code> array passed 6285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to the <code>format</code> methods or the result array returned 6295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * by the <code>parse</code> methods. 6305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If the argument index is used for more than one format element 6325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in the pattern string, then the new format is used for all such 6335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format elements. If the argument index is not used for any format 6345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * element in the pattern string, then the new format is ignored. 6355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 6365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is only supported when exclusively numbers are used for 6375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument names. Otherwise an IllegalArgumentException is thrown. 6385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 6395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param argumentIndex the argument index for which to use the new format 6405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormat the new format to use 6415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 6425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 6435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) { 6455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern.hasNamedArguments()) { 6465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 6475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "This method is not available in MessageFormat objects " + 6485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "that use alphanumeric argument names."); 6495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern.getPart(partIndex + 1).getValue() == argumentIndex) { 6525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormat); 6535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 6575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Sets the Format object to use for the format elements within the 6595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * previously set pattern string that use the given argument 6605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * name. 6615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If the argument name is used for more than one format element 6635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in the pattern string, then the new format is used for all such 6645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format elements. If the argument name is not used for any format 6655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * element in the pattern string, then the new format is ignored. 6665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This API may be used on formats that do not use named arguments. 6685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In this case <code>argumentName</code> should be a String that names 6695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * an argument index, e.g. "0", "1", "2"... etc. If it does not name 6705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a valid index, the format will be ignored. No error is thrown. 6715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 6725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param argumentName the name of the argument to change 6735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormat the new format to use 6745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 6755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormatByArgumentName(String argumentName, Format newFormat) { 6775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber = MessagePattern.validateArgumentName(argumentName); 6785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) { 6795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 6805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 6825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNameMatches(partIndex + 1, argumentName, argNumber)) { 6835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormat); 6845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 6885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets the Format object to use for the format element with the given 6905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format element index within the previously set pattern string. 6915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The format element index is the zero-based number of the format 6925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * element counting from the start of the pattern string. 6935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Since the order of format elements in a pattern string often 6955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * changes during localization, it is generally better to use the 6965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@link #setFormatByArgumentIndex setFormatByArgumentIndex} 6975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * method, which accesses format elements based on the argument 6985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * index they specify. 6995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param formatElementIndex the index of a format element within the pattern 7015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param newFormat the format to use for the specified format element 7025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or 7035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * larger than the number of format elements in the pattern string 7045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 7055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void setFormat(int formatElementIndex, Format newFormat) { 7075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int formatNumber = 0; 7085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (formatNumber == formatElementIndex) { 7105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setCustomArgStartFormat(partIndex, newFormat); 7115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 7125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++formatNumber; 7145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new ArrayIndexOutOfBoundsException(formatElementIndex); 7165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the Format objects used for the values passed into 7205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>format</code> methods or returned from <code>parse</code> 7215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * methods. The indices of elements in the returned array 7225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * correspond to the argument indices used in the previously set 7235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern string. 7245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The order of formats in the returned array thus corresponds to 7255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the order of elements in the <code>arguments</code> array passed 7265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to the <code>format</code> methods or the result array returned 7275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * by the <code>parse</code> methods. 7285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 7295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If an argument index is used for more than one format element 7305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in the pattern string, then the format used for the last such 7315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format element is returned in the array. If an argument index 7325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is not used for any format element in the pattern string, then 7335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * null is returned in the array. 7345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is only supported when exclusively numbers are used for 7365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument names. Otherwise an IllegalArgumentException is thrown. 7375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the formats used for the arguments within the pattern 7395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 7405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 7415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Format[] getFormatsByArgumentIndex() { 7435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern.hasNamedArguments()) { 7445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 7455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "This method is not available in MessageFormat objects " + 7465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "that use alphanumeric argument names."); 7475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArrayList<Format> list = new ArrayList<Format>(); 7495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber = msgPattern.getPart(partIndex + 1).getValue(); 7515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while (argNumber >= list.size()) { 7525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath list.add(null); 7535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath list.set(argNumber, cachedFormatters == null ? null : cachedFormatters.get(partIndex)); 7555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return list.toArray(new Format[list.size()]); 7575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the Format objects used for the format elements in the 7615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * previously set pattern string. 7625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The order of formats in the returned array corresponds to 7635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the order of format elements in the pattern string. 7645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 7655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Since the order of format elements in a pattern string often 7665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * changes during localization, it's generally better to use the 7675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@link #getFormatsByArgumentIndex()} 7685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * method, which assumes an order of formats corresponding to the 7695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * order of elements in the <code>arguments</code> array passed to 7705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the <code>format</code> methods or the result array returned by 7715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the <code>parse</code> methods. 7725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is only supported when exclusively numbers are used for 7745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument names. Otherwise an IllegalArgumentException is thrown. 7755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 7765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the formats used for the format elements in the pattern 7775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 7785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 7795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Format[] getFormats() { 7815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArrayList<Format> list = new ArrayList<Format>(); 7825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath list.add(cachedFormatters == null ? null : cachedFormatters.get(partIndex)); 7845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return list.toArray(new Format[list.size()]); 7865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Returns the top-level argument names. For more details, see 7905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@link #setFormatByArgumentName(String, Format)}. 7915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return a Set of argument names 7925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Set<String> getArgumentNames() { 7955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Set<String> result = new HashSet<String>(); 7965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 7975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath result.add(getArgName(partIndex + 1)); 7985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 8005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Returns the first top-level format associated with the given argument name. 8045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For more details, see {@link #setFormatByArgumentName(String, Format)}. 8055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param argumentName The name of the desired argument. 8065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the Format associated with the name, or null if there isn't one. 8075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Format getFormatByArgumentName(String argumentName) { 8105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (cachedFormatters == null) { 8115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 8125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber = MessagePattern.validateArgumentName(argumentName); 8145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNumber < MessagePattern.ARG_NAME_NOT_NUMBER) { 8155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 8165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 8185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNameMatches(partIndex + 1, argumentName, argNumber)) { 8195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return cachedFormatters.get(partIndex); 8205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 8235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Formats an array of objects and appends the <code>MessageFormat</code>'s 8275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern, with arguments replaced by the formatted objects, to the 8285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * provided <code>StringBuffer</code>. 8295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 8305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The text substituted for the individual format elements is derived from 8315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the current subformat of the format element and the 8325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> element at the format element's argument index 8335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as indicated by the first matching line of the following table. An 8345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument is <i>unavailable</i> if <code>arguments</code> is 8355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>null</code> or has fewer than argumentIndex+1 elements. When 8365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * an argument is unavailable no substitution is performed. 8375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 8385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <table border=1> 8395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>argType or Format 8415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>value object 8425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>Formatted Text 8435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>any</i> 8455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>unavailable</i> 8465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>"{" + argNameOrNumber + "}"</code> 8475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>any</i> 8495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>null</code> 8505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>"null"</code> 8515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>custom Format <code>!= null</code> 8535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>any</i> 8545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>customFormat.format(argument)</code> 8555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>noneArg, or custom Format <code>== null</code> 8575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>instanceof Number</code> 8585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code> 8595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>noneArg, or custom Format <code>== null</code> 8615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>instanceof Date</code> 8625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, 8635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * DateFormat.SHORT, getLocale()).format(argument)</code> 8645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>noneArg, or custom Format <code>== null</code> 8665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>instanceof String</code> 8675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>argument</code> 8685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>noneArg, or custom Format <code>== null</code> 8705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>any</i> 8715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><code>argument.toString()</code> 8725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 8735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>complexArg 8745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td><i>any</i> 8755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>result of recursive formatting of a selected sub-message 8765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </table> 8775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 8785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If <code>pos</code> is non-null, and refers to 8795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>Field.ARGUMENT</code>, the location of the first formatted 8805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * string will be returned. 8815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 8825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This method is only supported when the format does not use named 8835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments, otherwise an IllegalArgumentException is thrown. 8845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 8855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param arguments an array of objects to be formatted and substituted. 8865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param result where text is appended. 8875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos On input: an alignment field, if desired. 8885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * On output: the offsets of the alignment field. 8895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if a value in the 8905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> array is not of the type 8915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the corresponding argument or custom Format object. 8925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 8935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 8945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public final StringBuffer format(Object[] arguments, StringBuffer result, 8965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath FieldPosition pos) 8975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { 8985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format(arguments, null, new AppendableWrapper(result), pos); 8995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 9005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Formats a map of objects and appends the <code>MessageFormat</code>'s 9045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern, with arguments replaced by the formatted objects, to the 9055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * provided <code>StringBuffer</code>. 9065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 9075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The text substituted for the individual format elements is derived from 9085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the current subformat of the format element and the 9095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> value corresopnding to the format element's 9105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument name. 9115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 9125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A numbered pattern argument is matched with a map key that contains that number 9135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as an ASCII-decimal-digit string (without leading zero). 9145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 9155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * An argument is <i>unavailable</i> if <code>arguments</code> is 9165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>null</code> or does not have a value corresponding to an argument 9175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * name in the pattern. When an argument is unavailable no substitution 9185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is performed. 9195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 9205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param arguments a map of objects to be formatted and substituted. 9215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param result where text is appended. 9225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos On input: an alignment field, if desired. 9235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * On output: the offsets of the alignment field. 9245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if a value in the 9255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> array is not of the type 9265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the corresponding argument or custom Format object. 9275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the passed-in StringBuffer 9285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 9295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 9305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public final StringBuffer format(Map<String, Object> arguments, StringBuffer result, 9315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath FieldPosition pos) { 9325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format(null, arguments, new AppendableWrapper(result), pos); 9335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 9345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Creates a MessageFormat with the given pattern and uses it 9385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to format the given arguments. This is equivalent to 9395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote> 9405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link 9415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) 9425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format}(arguments, new StringBuffer(), null).toString()</code> 9435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </blockquote> 9445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 9455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if the pattern is invalid 9465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if a value in the 9475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> array is not of the type 9485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the corresponding argument or custom Format object. 9495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 9505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 9515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 9525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static String format(String pattern, Object... arguments) { 9535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessageFormat temp = new MessageFormat(pattern); 9545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return temp.format(arguments); 9555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Creates a MessageFormat with the given pattern and uses it to 9595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * format the given arguments. The pattern must identifyarguments 9605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * by name instead of by number. 9615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 9625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if the pattern is invalid 9635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if a value in the 9645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> array is not of the type 9655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the corresponding argument or custom Format object. 9665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see #format(Map, StringBuffer, FieldPosition) 9675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see #format(String, Object[]) 9685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 9695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 9705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static String format(String pattern, Map<String, Object> arguments) { 9715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessageFormat temp = new MessageFormat(pattern); 9725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return temp.format(arguments); 9735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Returns true if this MessageFormat uses named arguments, 9775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and false otherwise. See class description. 9785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 9795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if named arguments are used. 9805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 9815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 9825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean usesNamedArguments() { 9835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msgPattern.hasNamedArguments(); 9845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Overrides 9875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Formats a map or array of objects and appends the <code>MessageFormat</code>'s 9895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern, with format elements replaced by the formatted objects, to the 9905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * provided <code>StringBuffer</code>. 9915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is equivalent to either of 9925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote> 9935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, 9945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code> 9955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>{@link #format(java.util.Map, java.lang.StringBuffer, 9965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * java.text.FieldPosition) format}((Map) arguments, result, pos)</code> 9975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </blockquote> 9985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A map must be provided if this format uses named arguments, otherwise 9995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * an IllegalArgumentException will be thrown. 10005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param arguments a map or array of objects to be formatted 10015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param result where text is appended 10025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos On input: an alignment field, if desired 10035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * On output: the offsets of the alignment field 10045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if an argument in 10055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> is not of the type 10065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the format element(s) that use it 10075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if <code>arguments<code> is 10085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * an array of Object and this format uses named arguments 10095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 10105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 10115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public final StringBuffer format(Object arguments, StringBuffer result, 10125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath FieldPosition pos) 10135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { 10145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format(arguments, new AppendableWrapper(result), pos); 10155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 10165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 10185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 10195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Formats an array of objects and inserts them into the 10205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>MessageFormat</code>'s pattern, producing an 10215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>AttributedCharacterIterator</code>. 10225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * You can use the returned <code>AttributedCharacterIterator</code> 10235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to build the resulting String, as well as to determine information 10245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * about the resulting String. 10255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 10265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The text of the returned <code>AttributedCharacterIterator</code> is 10275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the same that would be returned by 10285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <blockquote> 10295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, 10305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code> 10315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </blockquote> 10325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 10335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In addition, the <code>AttributedCharacterIterator</code> contains at 10345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * least attributes indicating where text was generated from an 10355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument in the <code>arguments</code> array. The keys of these attributes are of 10365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * type <code>MessageFormat.Field</code>, their values are 10375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>Integer</code> objects indicating the index in the <code>arguments</code> 10385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * array of the argument from which the text was generated. 10395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 10405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The attributes/value from the underlying <code>Format</code> 10415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * instances that <code>MessageFormat</code> uses will also be 10425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * placed in the resulting <code>AttributedCharacterIterator</code>. 10435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This allows you to not only find where an argument is placed in the 10445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * resulting String, but also which fields it contains in turn. 10455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 10465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param arguments an array of objects to be formatted and substituted. 10475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return AttributedCharacterIterator describing the formatted value. 10485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception NullPointerException if <code>arguments</code> is null. 10495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if a value in the 10505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> array is not of the type 10515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the corresponding argument or custom Format object. 10525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 10535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 10545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public AttributedCharacterIterator formatToCharacterIterator(Object arguments) { 10555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (arguments == null) { 10565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new NullPointerException( 10575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "formatToCharacterIterator must be passed non-null object"); 10585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder result = new StringBuilder(); 10605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AppendableWrapper wrapper = new AppendableWrapper(result); 10615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath wrapper.useAttributes(); 10625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format(arguments, wrapper, null); 10635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AttributedString as = new AttributedString(result.toString()); 10645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (AttributeAndPosition a : wrapper.attributes) { 10655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath as.addAttribute(a.key, a.value, a.start, a.limit); 10665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return as.getIterator(); 10685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 10705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 10715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses the string. 10725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 10735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Caveats: The parse may fail in a number of circumstances. 10745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For example: 10755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <ul> 10765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>If one of the arguments does not occur in the pattern. 10775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>If the format of an argument loses information, such as 10785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with a choice format where a large number formats to "many". 10795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>Does not yet handle recursion (where 10805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the substituted strings contain {n} references.) 10815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>Will not always find a match (or the correct match) 10825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * if some part of the parse is ambiguous. 10835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For example, if the pattern "{1},{2}" is used with the 10845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * string arguments {"a,b", "c"}, it will format as "a,b,c". 10855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * When the result is parsed, it will return {"a", "b,c"}. 10865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>If a single argument is parsed more than once in the string, 10875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * then the later parse wins. 10885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </ul> 10895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * When the parse fails, use ParsePosition.getErrorIndex() to find out 10905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * where in the string did the parsing failed. The returned error 10915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * index is the starting offset of the sub-patterns that the string 10925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is comparing with. For example, if the parsing string "AAA {0} BBB" 10935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is comparing against the pattern "AAD {0} BBB", the error index is 10945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 0. When an error occurs, the call to this method will return null. 10955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If the source is null, return an empty array. 10965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 10975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if this format uses named arguments 10985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 10995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 11005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Object[] parse(String source, ParsePosition pos) { 11015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern.hasNamedArguments()) { 11025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "This method is not available in MessageFormat objects " + 11045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "that use named argument."); 11055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Count how many slots we need in the array. 11085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int maxArgId = -1; 11095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { 11105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber=msgPattern.getPart(partIndex + 1).getValue(); 11115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (argNumber > maxArgId) { 11125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath maxArgId = argNumber; 11135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object[] resultArray = new Object[maxArgId + 1]; 11165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int backupStartPos = pos.getIndex(); 11185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parse(0, source, pos, resultArray, null); 11195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (pos.getIndex() == backupStartPos) { // unchanged, returned object is null 11205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 11215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return resultArray; 11245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 11275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Parses the string, returning the results in a Map. 11285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is similar to the version that returns an array 11295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * of Object. This supports both named and numbered 11305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments-- if numbered, the keys in the map are the 11315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...). 11325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 11335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param source the text to parse 11345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos the position at which to start parsing. on return, 11355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * contains the result of the parse. 11365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return a Map containing key/value pairs for each parsed argument. 11375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 11385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 11395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Map<String, Object> parseToMap(String source, ParsePosition pos) { 11405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Map<String, Object> result = new HashMap<String, Object>(); 11415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int backupStartPos = pos.getIndex(); 11425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parse(0, source, pos, null, result); 11435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (pos.getIndex() == backupStartPos) { 11445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 11455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 11475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 11505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses text from the beginning of the given string to produce an object 11515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * array. 11525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The method may not use the entire text of the given string. 11535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 11545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * See the {@link #parse(String, ParsePosition)} method for more information 11555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * on message parsing. 11565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 11575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param source A <code>String</code> whose beginning should be parsed. 11585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return An <code>Object</code> array parsed from the string. 11595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception ParseException if the beginning of the specified string cannot be parsed. 11605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @exception IllegalArgumentException if this format uses named arguments 11615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 11625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 11635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Object[] parse(String source) throws ParseException { 11645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ParsePosition pos = new ParsePosition(0); 11655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object[] result = parse(source, pos); 11665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (pos.getIndex() == 0) // unchanged, returned object is null 11675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new ParseException("MessageFormat parse error!", 11685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.getErrorIndex()); 11695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 11715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 11745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses the string, filling either the Map or the Array. 11755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is a private method that all the public parsing methods call. 11765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This supports both named and numbered 11775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments-- if numbered, the keys in the map are the 11785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * corresponding ASCII-decimal-digit strings (e.g. "0", "1", "2"...). 11795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 11805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param msgStart index in the message pattern to start from. 11815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param source the text to parse 11825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos the position at which to start parsing. on return, 11835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * contains the result of the parse. 11845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param args if not null, the parse results will be filled here (The pattern 11855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * has to have numbered arguments in order for this to not be null). 11865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param argsMap if not null, the parse results will be filled here. 11875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 11885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void parse(int msgStart, String source, ParsePosition pos, 11895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object[] args, Map<String, Object> argsMap) { 11905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (source == null) { 11915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 11925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String msgString=msgPattern.getPatternString(); 11945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevIndex=msgPattern.getPart(msgStart).getLimit(); 11955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int sourceOffset = pos.getIndex(); 11965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ParsePosition tempStatus = new ParsePosition(0); 11975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=msgStart+1; ; ++i) { 11995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=msgPattern.getPart(i); 12005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.getType(); 12015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index=part.getIndex(); 12025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Make sure the literal string matches. 12035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int len = index - prevIndex; 12045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (len == 0 || msgString.regionMatches(prevIndex, source, sourceOffset, len)) { 12055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sourceOffset += len; 12065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex += len; 12075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setErrorIndex(sourceOffset); 12095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; // leave index as is to signal error 12105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.MSG_LIMIT) { 12125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Things went well! Done. 12135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setIndex(sourceOffset); 12145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 12155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR) { 12175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex=part.getLimit(); 12185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; 12195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // We do not support parsing Plural formats. (No REPLACE_NUMBER here.) 12215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert type==Part.Type.ARG_START : "Unexpected Part "+part+" in parsed message."; 12225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argLimit=msgPattern.getLimitPartIndex(i); 12235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 12245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArgType argType=part.getArgType(); 12255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath part=msgPattern.getPart(++i); 12265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Compute the argId, so we can use it as a key. 12275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object argId=null; 12285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber = 0; 12295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String key = null; 12305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(args!=null) { 12315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argNumber=part.getValue(); // ARG_NUMBER 12325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argId = Integer.valueOf(argNumber); 12335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(part.getType()==MessagePattern.Part.Type.ARG_NAME) { 12355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath key=msgPattern.getSubstring(part); 12365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else /* ARG_NUMBER */ { 12375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath key=Integer.toString(part.getValue()); 12385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argId = key; 12405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 12425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++i; 12435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Format formatter = null; 12445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean haveArgResult = false; 12455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object argResult = null; 12465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) { 12475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Just parse using the formatter. 12485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath tempStatus.setIndex(sourceOffset); 12495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argResult = formatter.parseObject(source, tempStatus); 12505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (tempStatus.getIndex() == sourceOffset) { 12515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setErrorIndex(sourceOffset); 12525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; // leave index as is to signal error 12535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath haveArgResult = true; 12555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sourceOffset = tempStatus.getIndex(); 12565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if( 12575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType==ArgType.NONE || 12585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) { 12595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Match as a string. 12605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // if at end, use longest possible match 12615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // otherwise uses first match to intervening string 12625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // does NOT recursively try all possibilities 12635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String stringAfterArgument = getLiteralStringUntilNextArgument(argLimit); 12645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int next; 12655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (stringAfterArgument.length() != 0) { 12665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath next = source.indexOf(stringAfterArgument, sourceOffset); 12675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath next = source.length(); 12695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (next < 0) { 12715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setErrorIndex(sourceOffset); 12725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; // leave index as is to signal error 12735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String strValue = source.substring(sourceOffset, next); 12755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (!strValue.equals("{" + argId.toString() + "}")) { 12765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath haveArgResult = true; 12775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argResult = strValue; 12785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sourceOffset = next; 12805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType==ArgType.CHOICE) { 12825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath tempStatus.setIndex(sourceOffset); 12835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double choiceResult = parseChoiceArgument(msgPattern, i, source, tempStatus); 12845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (tempStatus.getIndex() == sourceOffset) { 12855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setErrorIndex(sourceOffset); 12865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; // leave index as is to signal error 12875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argResult = choiceResult; 12895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath haveArgResult = true; 12905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sourceOffset = tempStatus.getIndex(); 12915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType.hasPluralStyle() || argType==ArgType.SELECT) { 12925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // No can do! 12935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new UnsupportedOperationException( 12945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Parsing of plural/select/selectordinal argument is not supported."); 12955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // This should never happen. 12975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalStateException("unexpected argType "+argType); 12985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (haveArgResult) { 13005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (args != null) { 13015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath args[argNumber] = argResult; 13025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (argsMap != null) { 13035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argsMap.put(key, argResult); 13045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex=msgPattern.getPart(argLimit).getLimit(); 13075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath i=argLimit; 13085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Parses text from the beginning of the given string to produce a map from 13135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument to values. The method may not use the entire text of the given string. 13145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 13155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>See the {@link #parse(String, ParsePosition)} method for more information on 13165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * message parsing. 13175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 13185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param source A <code>String</code> whose beginning should be parsed. 13195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return A <code>Map</code> parsed from the string. 13205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws ParseException if the beginning of the specified string cannot 13215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * be parsed. 13225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see #parseToMap(String, ParsePosition) 13235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 13245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 13255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Map<String, Object> parseToMap(String source) throws ParseException { 13265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ParsePosition pos = new ParsePosition(0); 13275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Map<String, Object> result = new HashMap<String, Object>(); 13285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parse(0, source, pos, null, result); 13295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (pos.getIndex() == 0) // unchanged, returned object is null 13305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new ParseException("MessageFormat parse error!", 13315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.getErrorIndex()); 13325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return result; 13345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses text from a string to produce an object array or Map. 13385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 13395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The method attempts to parse text starting at the index given by 13405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>pos</code>. 13415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If parsing succeeds, then the index of <code>pos</code> is updated 13425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * to the index after the last character used (parsing does not necessarily 13435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * use all characters up to the end of the string), and the parsed 13445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * object array is returned. The updated <code>pos</code> can be used to 13455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * indicate the starting point for the next call to this method. 13465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If an error occurs, then the index of <code>pos</code> is not 13475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * changed, the error index of <code>pos</code> is set to the index of 13485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the character where the error occurred, and null is returned. 13495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 13505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * See the {@link #parse(String, ParsePosition)} method for more information 13515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * on message parsing. 13525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 13535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param source A <code>String</code>, part of which should be parsed. 13545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pos A <code>ParsePosition</code> object with index and error 13555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * index information as described above. 13565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return An <code>Object</code> parsed from the string, either an 13575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * array of Object, or a Map, depending on whether named 13585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * arguments are used. This can be queried using <code>usesNamedArguments</code>. 13595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * In case of error, returns null. 13605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NullPointerException if <code>pos</code> is null. 13615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 13625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 13635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Object parseObject(String source, ParsePosition pos) { 13645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (!msgPattern.hasNamedArguments()) { 13655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parse(source, pos); 13665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parseToMap(source, pos); 13685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@inheritDoc} 13735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 13745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 13755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean equals(Object obj) { 13765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (this == obj) // quick check 13775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return true; 13785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (obj == null || getClass() != obj.getClass()) 13795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return false; 13805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessageFormat other = (MessageFormat) obj; 13815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return Utility.objectEquals(ulocale, other.ulocale) 13825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath && Utility.objectEquals(msgPattern, other.msgPattern) 13835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath && Utility.objectEquals(cachedFormatters, other.cachedFormatters) 13845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath && Utility.objectEquals(customFormatArgStarts, other.customFormatArgStarts); 13855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Note: It might suffice to only compare custom formatters 13865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // rather than all formatters. 13875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13888d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath */ 13895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@inheritDoc} 13925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.0 13935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 13945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 13955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int hashCode() { 13965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msgPattern.getPatternString().hashCode(); // enough for reasonable distribution 13975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Defines constants that are used as attribute keys in the 14015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>AttributedCharacterIterator</code> returned 14025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * from <code>MessageFormat.formatToCharacterIterator</code>. 14035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 14055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static class Field extends Format.Field { 14075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final long serialVersionUID = 7510380454602616157L; 14095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Create a <code>Field</code> with the specified name. 14125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param name The name of the attribute 14145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 14165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath protected Field(String name) { 14185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath super(name); 14195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Resolves instances being deserialized to the predefined constants. 14235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return resolved MessageFormat.Field constant 14255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws InvalidObjectException if the constant could not be resolved. 14265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 14285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath protected Object readResolve() throws InvalidObjectException { 14305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (this.getClass() != MessageFormat.Field.class) { 14315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new InvalidObjectException( 14325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "A subclass of MessageFormat.Field must implement readResolve."); 14335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (this.getName().equals(ARGUMENT.getName())) { 14355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARGUMENT; 14365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 14375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new InvalidObjectException("Unknown attribute name."); 14385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constant identifying a portion of a message that was generated 14435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * from an argument passed into <code>formatToCharacterIterator</code>. 14445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value associated with the key will be an <code>Integer</code> 14455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * indicating the index in the <code>arguments</code> array of the 14465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument from which the text was generated. 14475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 14485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.8 14495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static final Field ARGUMENT = new Field("message argument field"); 14515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // ===========================privates============================ 14545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // *Important*: All fields must be declared *transient* so that we can fully 14565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // control serialization! 14575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // See for example Joshua Bloch's "Effective Java", chapter 10 Serialization. 14585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The locale to use for formatting numbers and dates. 14615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14628d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath private transient Locale locale_; 14635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The MessagePattern which contains the parsed structure of the pattern string. 14665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient MessagePattern msgPattern; 14685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Cached formatters so we can just use them whenever needed instead of creating 14705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * them from scratch every time. 14715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient Map<Integer, Format> cachedFormatters; 14735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Set of ARG_START part indexes where custom, user-provided Format objects 14755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * have been set via setFormat() or similar API. 14765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient Set<Integer> customFormatArgStarts; 14785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Stock formatters. Those are used when a format is not explicitly mentioned in 14815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the message. The format is inferred from the argument. 14825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient DateFormat stockDateFormatter; 14845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient NumberFormat stockNumberFormatter; 14855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient PluralSelectorProvider pluralProvider; 14875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private transient PluralSelectorProvider ordinalProvider; 14885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private DateFormat getStockDateFormatter() { 14905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (stockDateFormatter == null) { 14915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath stockDateFormatter = DateFormat.getDateTimeInstance( 14928d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath DateFormat.SHORT, DateFormat.SHORT, locale_);//fix 14935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return stockDateFormatter; 14955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private NumberFormat getStockNumberFormatter() { 14975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (stockNumberFormatter == null) { 14988d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath stockNumberFormatter = NumberFormat.getInstance(locale_); 14995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return stockNumberFormatter; 15015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // *Important*: All fields must be declared *transient*. 15045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // See the longer comment above ulocale. 15055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 15075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Formats the arguments and writes the result into the 15085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * AppendableWrapper, updates the field position. 15095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 15105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>Exactly one of args and argsMap must be null, the other non-null. 15115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 15125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param msgStart Index to msgPattern part to start formatting from. 15135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pluralNumber null except when formatting a plural argument sub-message 15145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * where a '#' is replaced by the format string for this number. 15155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param args The formattable objects array. Non-null iff numbered values are used. 15165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param argsMap The key-value map of formattable objects. Non-null iff named values are used. 15175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param dest Output parameter to receive the result. 15185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The result (string & attributes) is appended to existing contents. 15195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param fp Field position status. 15205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 15215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void format(int msgStart, PluralSelectorContext pluralNumber, 15228d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath Object[] args, Map<String, Object> argsMap, Object[] nameValuePairs, 15235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AppendableWrapper dest, FieldPosition fp) { 15245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String msgString=msgPattern.getPatternString(); 15255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevIndex=msgPattern.getPart(msgStart).getLimit(); 15265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=msgStart+1;; ++i) { 15275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=msgPattern.getPart(i); 15285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.getType(); 15295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index=part.getIndex(); 15305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.append(msgString, prevIndex, index); 15315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.MSG_LIMIT) { 15325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 15335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex=part.getLimit(); 15355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.REPLACE_NUMBER) { 15365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(pluralNumber.forReplaceNumber) { 15375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // number-offset was already formatted. 15385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(pluralNumber.formatter, 15395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pluralNumber.number, pluralNumber.numberString); 15405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(getStockNumberFormatter(), pluralNumber.number); 15425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; 15445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type!=Part.Type.ARG_START) { 15465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; 15475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argLimit=msgPattern.getLimitPartIndex(i); 15495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArgType argType=part.getArgType(); 15505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath part=msgPattern.getPart(++i); 15515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object arg; 15525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean noArg=false; 15535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Object argId=null; 15545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String argName=msgPattern.getSubstring(part); 15555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(args!=null) { 15565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argNumber=part.getValue(); // ARG_NUMBER 15575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (dest.attributes != null) { 15585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // We only need argId if we add it into the attributes. 15595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argId = Integer.valueOf(argNumber); 15605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(0<=argNumber && argNumber<args.length) { 15625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath arg=args[argNumber]; 15635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath arg=null; 15655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath noArg=true; 15665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15678d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } else if(nameValuePairs!=null) { 15688d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath argId = argName; 15698d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath for(int nvIndex=0;; nvIndex+=2) { 15708d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath if(nvIndex<nameValuePairs.length) { 15718d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath if(argName.equals(nameValuePairs[nvIndex].toString())) { 15728d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath arg=nameValuePairs[nvIndex+1]; 15738d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath break; 15748d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } 15758d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } else { 15768d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath arg=null; 15778d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath noArg=true; 15788d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath break; 15798d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } 15808d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } 15815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argId = argName; 15835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argsMap!=null && argsMap.containsKey(argName)) { 15845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath arg=argsMap.get(argName); 15855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath arg=null; 15875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath noArg=true; 15885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++i; 15915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevDestLength=dest.length; 15925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Format formatter = null; 15935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (noArg) { 15945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.append("{"+argName+"}"); 15955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (arg == null) { 15965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.append("null"); 15975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(pluralNumber!=null && pluralNumber.numberArgIndex==(i-2)) { 15985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(pluralNumber.offset == 0) { 15995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The number was already formatted with this formatter. 16005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(pluralNumber.formatter, pluralNumber.number, pluralNumber.numberString); 16015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 16025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Do not use the formatted (number-offset) string for a named argument 16035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // that formats the number without subtracting the offset. 16045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(pluralNumber.formatter, arg); 16055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(cachedFormatters!=null && (formatter=cachedFormatters.get(i - 2))!=null) { 16075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. 16088d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath { 16095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(formatter, arg); 16105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if( 16125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType==ArgType.NONE || 16135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (cachedFormatters!=null && cachedFormatters.containsKey(i - 2))) { 16145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // ArgType.NONE, or 16155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // any argument which got reset to null via setFormat() or its siblings. 16165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (arg instanceof Number) { 16175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // format number if can 16185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(getStockNumberFormatter(), arg); 16195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (arg instanceof Date) { 16205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // format a Date if can 16215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.formatAndAppend(getStockDateFormatter(), arg); 16225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 16235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.append(arg.toString()); 16245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType==ArgType.CHOICE) { 16265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (!(arg instanceof Number)) { 16275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("'" + arg + "' is not a Number"); 16285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double number = ((Number)arg).doubleValue(); 16305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int subMsgStart=findChoiceSubMessage(msgPattern, i, number); 16318d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath formatComplexSubMessage(subMsgStart, null, args, argsMap, nameValuePairs, dest); 16325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType.hasPluralStyle()) { 16335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (!(arg instanceof Number)) { 16345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("'" + arg + "' is not a Number"); 16355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath PluralSelectorProvider selector; 16375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType == ArgType.PLURAL) { 16385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (pluralProvider == null) { 16395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pluralProvider = new PluralSelectorProvider(this, PluralType.CARDINAL); 16405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath selector = pluralProvider; 16425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 16435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (ordinalProvider == null) { 16445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ordinalProvider = new PluralSelectorProvider(this, PluralType.ORDINAL); 16455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath selector = ordinalProvider; 16475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Number number = (Number)arg; 16495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double offset=msgPattern.getPluralOffset(i); 16505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath PluralSelectorContext context = 16515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath new PluralSelectorContext(i, argName, number, offset); 16525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int subMsgStart=PluralFormat.findSubMessage( 16535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern, i, selector, context, number.doubleValue()); 16548d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath formatComplexSubMessage(subMsgStart, context, args, argsMap, nameValuePairs, dest); 16555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType==ArgType.SELECT) { 16565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int subMsgStart=SelectFormat.findSubMessage(msgPattern, i, arg.toString()); 16578d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath formatComplexSubMessage(subMsgStart, null, args, argsMap, nameValuePairs, dest); 16585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 16595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // This should never happen. 16605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalStateException("unexpected argType "+argType); 16615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath fp = updateMetaData(dest, prevDestLength, fp, argId); 16635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex=msgPattern.getPart(argLimit).getLimit(); 16645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath i=argLimit; 16655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 16685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void formatComplexSubMessage( 16695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int msgStart, PluralSelectorContext pluralNumber, 16708d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath Object[] args, Map<String, Object> argsMap, Object[] nameValuePairs, 16715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AppendableWrapper dest) { 16725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (!msgPattern.jdkAposMode()) { 16738d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath format(msgStart, pluralNumber, args, argsMap, nameValuePairs, dest, null); 16745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 16755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // JDK compatibility mode: (see JDK MessageFormat.format() API docs) 16778d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath throw new UnsupportedOperationException("JDK apostrophe mode not supported"); 16788d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath /* 16795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // - remove SKIP_SYNTAX; that is, remove half of the apostrophes 16805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // - if the result string contains an open curly brace '{' then 16815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // instantiate a temporary MessageFormat object and format again; 16825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // otherwise just append the result string 16835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String msgString = msgPattern.getPatternString(); 16845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String subMsgString; 16855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder sb = null; 16865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevIndex = msgPattern.getPart(msgStart).getLimit(); 16875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int i = msgStart;;) { 16885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part = msgPattern.getPart(++i); 16895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type = part.getType(); 16905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index = part.getIndex(); 16915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (type == Part.Type.MSG_LIMIT) { 16925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (sb == null) { 16935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath subMsgString = msgString.substring(prevIndex, index); 16945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 16955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath subMsgString = sb.append(msgString, prevIndex, index).toString(); 16965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 16975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 16985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (type == Part.Type.REPLACE_NUMBER || type == Part.Type.SKIP_SYNTAX) { 16995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (sb == null) { 17005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb = new StringBuilder(); 17015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(msgString, prevIndex, index); 17035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (type == Part.Type.REPLACE_NUMBER) { 17045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(pluralNumber.forReplaceNumber) { 17055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // number-offset was already formatted. 17065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(pluralNumber.numberString); 17075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 17085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(getStockNumberFormatter().format(pluralNumber.number)); 17095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex = part.getLimit(); 17125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if (type == Part.Type.ARG_START) { 17135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (sb == null) { 17145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb = new StringBuilder(); 17155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(msgString, prevIndex, index); 17175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex = index; 17185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath i = msgPattern.getLimitPartIndex(i); 17195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index = msgPattern.getPart(i).getLimit(); 17205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern.appendReducedApostrophes(msgString, prevIndex, index, sb); 17215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex = index; 17225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (subMsgString.indexOf('{') >= 0) { 17255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessageFormat subMsgFormat = new MessageFormat("", ulocale); 17265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath subMsgFormat.applyPattern(subMsgString, MessagePattern.ApostropheMode.DOUBLE_REQUIRED); 17275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath subMsgFormat.format(0, null, args, argsMap, dest, null); 17285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 17295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.append(subMsgString); 17305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17318d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath */ 17325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 17345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 17355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Read as much literal string from the pattern string as possible. This stops 17365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as soon as it finds an argument, or it reaches the end of the string. 17375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param from Index in the pattern string to start from. 17385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return A substring from the pattern string representing the longest possible 17395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * substring with no arguments. 17405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 17415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private String getLiteralStringUntilNextArgument(int from) { 17425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder b = new StringBuilder(); 17435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String msgString=msgPattern.getPatternString(); 17445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevIndex=msgPattern.getPart(from).getLimit(); 17455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=from+1;; ++i) { 17465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=msgPattern.getPart(i); 17475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.getType(); 17485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index=part.getIndex(); 17495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath b.append(msgString, prevIndex, index); 17505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.ARG_START || type==Part.Type.MSG_LIMIT) { 17515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return b.toString(); 17525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR : 17545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unexpected Part "+part+" in parsed message."; 17555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex=part.getLimit(); 17565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 17595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private FieldPosition updateMetaData(AppendableWrapper dest, int prevLength, 17605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath FieldPosition fp, Object argId) { 17615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (dest.attributes != null && prevLength < dest.length) { 17625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath dest.attributes.add(new AttributeAndPosition(argId, prevLength, dest.length)); 17635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (fp != null && Field.ARGUMENT.equals(fp.getFieldAttribute())) { 17655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath fp.setBeginIndex(prevLength); 17665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath fp.setEndIndex(dest.length); 17675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return null; 17685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return fp; 17705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 17725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // This lives here because ICU4J does not have its own ChoiceFormat class. 17735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 17745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Finds the ChoiceFormat sub-message for the given number. 17755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern A MessagePattern. 17765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param partIndex the index of the first ChoiceFormat argument style part. 17775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param number a number to be mapped to one of the ChoiceFormat argument's intervals 17785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the sub-message start part index. 17795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 17805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static int findChoiceSubMessage(MessagePattern pattern, int partIndex, double number) { 17815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int count=pattern.countParts(); 17825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int msgStart; 17835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples 17845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // until ARG_LIMIT or end of choice-only pattern. 17855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Ignore the first number and selector and start the loop on the first message. 17865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex+=2; 17875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 17885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Skip but remember the current sub-message. 17895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgStart=partIndex; 17905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex=pattern.getLimitPartIndex(partIndex); 17915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(++partIndex>=count) { 17925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Reached the end of the choice-only pattern. 17935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Return with the last sub-message. 17945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 17955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 17965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=pattern.getPart(partIndex++); 17975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.getType(); 17985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.ARG_LIMIT) { 17995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Reached the end of the ChoiceFormat style. 18005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Return with the last sub-message. 18015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 18025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // part is an ARG_INT or ARG_DOUBLE 18045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert type.hasNumericValue(); 18055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double boundary=pattern.getNumericValue(part); 18065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Fetch the ARG_SELECTOR character. 18075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int selectorIndex=pattern.getPatternIndex(partIndex++); 18085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char boundaryChar=pattern.getPatternString().charAt(selectorIndex); 18095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(boundaryChar=='<' ? !(number>boundary) : !(number>=boundary)) { 18105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The number is in the interval between the previous boundary and the current one. 18115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Return with the sub-message between them. 18125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The !(a>b) and !(a>=b) comparisons are equivalent to 18135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // (a<=b) and (a<b) except they "catch" NaN. 18145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 18155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msgStart; 18185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 18205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Ported from C++ ChoiceFormat::parse(). 18215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static double parseChoiceArgument( 18225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern pattern, int partIndex, 18235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String source, ParsePosition pos) { 18245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // find the best number (defined as the one with the longest parse) 18255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start = pos.getIndex(); 18265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int furthest = start; 18275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double bestNumber = Double.NaN; 18285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double tempNumber = 0.0; 18295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while (pattern.getPartType(partIndex) != Part.Type.ARG_LIMIT) { 18305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath tempNumber = pattern.getNumericValue(pattern.getPart(partIndex)); 18315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex += 2; // skip the numeric part and ignore the ARG_SELECTOR 18325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int msgLimit = pattern.getLimitPartIndex(partIndex); 18335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int len = matchStringUntilLimitPart(pattern, partIndex, msgLimit, source, start); 18345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (len >= 0) { 18355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int newIndex = start + len; 18365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (newIndex > furthest) { 18375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath furthest = newIndex; 18385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath bestNumber = tempNumber; 18395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (furthest == source.length()) { 18405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 18415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex = msgLimit + 1; 18455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (furthest == start) { 18475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setErrorIndex(start); 18485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 18495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath pos.setIndex(furthest); 18505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return bestNumber; 18525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 18545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 18555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Matches the pattern string from the end of the partIndex to 18565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the beginning of the limitPartIndex, 18575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * including all syntax except SKIP_SYNTAX, 18585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * against the source string starting at sourceOffset. 18595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If they match, returns the length of the source string match. 18605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Otherwise returns -1. 18615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 18625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static int matchStringUntilLimitPart( 18635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern pattern, int partIndex, int limitPartIndex, 18645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String source, int sourceOffset) { 18655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int matchingSourceLength = 0; 18665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String msgString = pattern.getPatternString(); 18675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevIndex = pattern.getPart(partIndex).getLimit(); 18685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (;;) { 18695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part = pattern.getPart(++partIndex); 18705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (partIndex == limitPartIndex || part.getType() == Part.Type.SKIP_SYNTAX) { 18715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index = part.getIndex(); 18725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length = index - prevIndex; 18735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (length != 0 && !source.regionMatches(sourceOffset, msgString, prevIndex, length)) { 18745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return -1; // mismatch 18755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath matchingSourceLength += length; 18775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (partIndex == limitPartIndex) { 18785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return matchingSourceLength; 18795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prevIndex = part.getLimit(); // SKIP_SYNTAX 18815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 18855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 18865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Finds the "other" sub-message. 18875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param partIndex the index of the first PluralFormat argument style part. 18885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the "other" sub-message start part index. 18895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 18905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int findOtherSubMessage(int partIndex) { 18915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int count=msgPattern.countParts(); 18925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern.Part part=msgPattern.getPart(partIndex); 18935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(part.getType().hasNumericValue()) { 18945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++partIndex; 18955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 18965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples 18975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // until ARG_LIMIT or end of plural-only pattern. 18985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath do { 18995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath part=msgPattern.getPart(partIndex++); 19005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern.Part.Type type=part.getType(); 19015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==MessagePattern.Part.Type.ARG_LIMIT) { 19025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 19035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert type==MessagePattern.Part.Type.ARG_SELECTOR; 19055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // part is an ARG_SELECTOR followed by an optional explicit value, and then a message 19065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(msgPattern.partSubstringMatches(part, "other")) { 19075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return partIndex; 19085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(msgPattern.getPartType(partIndex).hasNumericValue()) { 19105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++partIndex; // skip the numeric-value part of "=1" etc. 19115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath partIndex=msgPattern.getLimitPartIndex(partIndex); 19135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } while(++partIndex<count); 19145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 0; 19155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 19175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 19185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the ARG_START index of the first occurrence of the plural number in a sub-message. 19195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns -1 if it is a REPLACE_NUMBER. 19205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns 0 if there is neither. 19215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 19225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int findFirstPluralNumberArg(int msgStart, String argName) { 19235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=msgStart+1;; ++i) { 19245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=msgPattern.getPart(i); 19255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.getType(); 19265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.MSG_LIMIT) { 19275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 0; 19285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.REPLACE_NUMBER) { 19305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return -1; 19315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.ARG_START) { 19335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArgType argType=part.getArgType(); 19345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argName.length()!=0 && (argType==ArgType.NONE || argType==ArgType.SIMPLE)) { 19355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath part=msgPattern.getPart(i+1); // ARG_NUMBER or ARG_NAME 19365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(msgPattern.partSubstringMatches(part, argName)) { 19375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return i; 19385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath i=msgPattern.getLimitPartIndex(i); 19415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 19455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 19465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Mutable input/output values for the PluralSelectorProvider. 19475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Separate so that it is possible to make MessageFormat Freezable. 19485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 19495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final class PluralSelectorContext { 19505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private PluralSelectorContext(int start, String name, Number num, double off) { 19515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath startIndex = start; 19525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argName = name; 19535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // number needs to be set even when select() is not called. 19545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Keep it as a Number/Formattable: 19555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // For format() methods, and to preserve information (e.g., BigDecimal). 19565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(off == 0) { 19575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath number = num; 19585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 19595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath number = num.doubleValue() - off; 19605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath offset = off; 19625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 19645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String toString() { 19655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new AssertionError("PluralSelectorContext being formatted, rather than its number"); 19665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 19685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Input values for plural selection with decimals. 19695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int startIndex; 19705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String argName; 19715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** argument number - plural offset */ 19725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Number number; 19735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double offset; 19745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Output values for plural selection with decimals. 19755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */ 19765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int numberArgIndex; 19775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Format formatter; 19785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** formatted argument number - plural offset */ 19795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String numberString; 19805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** true if number-offset was formatted with the stock number formatter */ 19815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean forReplaceNumber; 19825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 19845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 19855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This provider helps defer instantiation of a PluralRules object 19865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * until we actually need to select a keyword. 19875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For example, if the number matches an explicit-value selector like "=1" 19885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * we do not need any PluralRules. 19895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 19905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final class PluralSelectorProvider implements PluralFormat.PluralSelector { 19915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public PluralSelectorProvider(MessageFormat mf, PluralType type) { 19925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgFormat = mf; 19935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath this.type = type; 19945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String select(Object ctx, double number) { 19965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(rules == null) { 19978d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath rules = PluralRules.forLocale(msgFormat.locale_, type); 19985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 19995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Select a sub-message according to how the number is formatted, 20005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // which is specified in the selected sub-message. 20015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // We avoid this circle by looking at how 20025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // the number is formatted in the "other" sub-message 20035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // which must always be present and usually contains the number. 20045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Message authors should be consistent across sub-messages. 20055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath PluralSelectorContext context = (PluralSelectorContext)ctx; 20065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int otherIndex = msgFormat.findOtherSubMessage(context.startIndex); 20075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName); 20085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != null) { 20095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath context.formatter = msgFormat.cachedFormatters.get(context.numberArgIndex); 20105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(context.formatter == null) { 20125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath context.formatter = msgFormat.getStockNumberFormatter(); 20135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath context.forReplaceNumber = true; 20145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert context.number.doubleValue() == number; // argument number minus the offset 20165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath context.numberString = context.formatter.format(context.number); 20178d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath /* TODO: Try to get FixedDecimal from formatted string. 20185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(context.formatter instanceof DecimalFormat) { 20195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath FixedDecimal dec = ((DecimalFormat)context.formatter).getFixedDecimal(number); 20205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return rules.select(dec); 20218d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath } else */ { 20225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return rules.select(number); 20235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private MessageFormat msgFormat; 20265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private PluralRules rules; 20275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private PluralType type; 20285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @SuppressWarnings("unchecked") 20315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void format(Object arguments, AppendableWrapper result, FieldPosition fp) { 20325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if ((arguments == null || arguments instanceof Map)) { 20335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format(null, (Map<String, Object>)arguments, result, fp); 20345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 20355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath format((Object[])arguments, null, result, fp); 20365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 20405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Internal routine used by format. 20415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 20425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException if an argument in the 20435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <code>arguments</code> map is not of the type 20445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * expected by the format element(s) that use it. 20455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 20465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void format(Object[] arguments, Map<String, Object> argsMap, 20475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AppendableWrapper dest, FieldPosition fp) { 20485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (arguments != null && msgPattern.hasNamedArguments()) { 20495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 20505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "This method is not available in MessageFormat objects " + 20515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "that use alphanumeric argument names."); 20525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20538d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath format(0, null, arguments, argsMap, null, dest, fp); 20545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void resetPattern() { 20575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (msgPattern != null) { 20585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msgPattern.clear(); 20595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (cachedFormatters != null) { 20615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath cachedFormatters.clear(); 20625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath customFormatArgStarts = null; 20645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 20655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final String[] typeList = 20675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { "number", "date", "time", "spellout", "ordinal", "duration" }; 20685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int 20695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_NUMBER = 0, 20705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_DATE = 1, 20715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_TIME = 2, 20725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_SPELLOUT = 3, 20735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_ORDINAL = 4, 20745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath TYPE_DURATION = 5; 20755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final String[] modifierList = 20775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath {"", "currency", "percent", "integer"}; 20785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int 20805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MODIFIER_EMPTY = 0, 20815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MODIFIER_CURRENCY = 1, 20825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MODIFIER_PERCENT = 2, 20835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MODIFIER_INTEGER = 3; 20845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final String[] dateModifierList = 20865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath {"", "short", "medium", "long", "full"}; 20875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int 20895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DATE_MODIFIER_EMPTY = 0, 20905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DATE_MODIFIER_SHORT = 1, 20915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DATE_MODIFIER_MEDIUM = 2, 20925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DATE_MODIFIER_LONG = 3, 20935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DATE_MODIFIER_FULL = 4; 20945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 20955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Creates an appropriate Format object for the type and style passed. 20965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Both arguments cannot be null. 20975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private Format createAppropriateFormat(String type, String style) { 20985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Format newFormat = null; 20995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int subformatType = findKeyword(type, typeList); 21005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (subformatType){ 21015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_NUMBER: 21025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (findKeyword(style, modifierList)) { 21035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case MODIFIER_EMPTY: 21048d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = NumberFormat.getInstance(locale_); 21055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case MODIFIER_CURRENCY: 21078d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = NumberFormat.getCurrencyInstance(locale_); 21085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case MODIFIER_PERCENT: 21108d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = NumberFormat.getPercentInstance(locale_); 21115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case MODIFIER_INTEGER: 21138d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = NumberFormat.getIntegerInstance(locale_); 21145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: // pattern 21165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newFormat = new DecimalFormat(style, 21178d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath new DecimalFormatSymbols(locale_)); 21185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_DATE: 21225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (findKeyword(style, dateModifierList)) { 21235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_EMPTY: 21248d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale_); 21255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_SHORT: 21278d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale_); 21285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_MEDIUM: 21308d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale_); 21315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_LONG: 21338d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getDateInstance(DateFormat.LONG, locale_); 21345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_FULL: 21368d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getDateInstance(DateFormat.FULL, locale_); 21375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: 21398d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = new SimpleDateFormat(style, locale_); 21405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_TIME: 21445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (findKeyword(style, dateModifierList)) { 21455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_EMPTY: 21468d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale_); 21475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_SHORT: 21498d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, locale_); 21505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_MEDIUM: 21528d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale_); 21535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_LONG: 21558d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getTimeInstance(DateFormat.LONG, locale_); 21565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case DATE_MODIFIER_FULL: 21588d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = DateFormat.getTimeInstance(DateFormat.FULL, locale_); 21595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: 21618d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath newFormat = new SimpleDateFormat(style, locale_); 21625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21658d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath /* There is no java.text.RuleBasedNumberFormat -- 21665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_SPELLOUT: 21675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { 21685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 21695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat.SPELLOUT); 21705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String ruleset = style.trim(); 21715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (ruleset.length() != 0) { 21725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 21735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath rbnf.setDefaultRuleSet(ruleset); 21745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath catch (Exception e) { 21765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // warn invalid ruleset 21775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newFormat = rbnf; 21805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_ORDINAL: 21835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { 21845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 21855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat.ORDINAL); 21865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String ruleset = style.trim(); 21875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (ruleset.length() != 0) { 21885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 21895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath rbnf.setDefaultRuleSet(ruleset); 21905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath catch (Exception e) { 21925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // warn invalid ruleset 21935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newFormat = rbnf; 21965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 21975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 21985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case TYPE_DURATION: 21995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath { 22005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ulocale, 22015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath RuleBasedNumberFormat.DURATION); 22025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String ruleset = style.trim(); 22035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (ruleset.length() != 0) { 22045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 22055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath rbnf.setDefaultRuleSet(ruleset); 22065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath catch (Exception e) { 22085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // warn invalid ruleset 22095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newFormat = rbnf; 22125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 22148d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath */ 22155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: 22165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Unknown format type \"" + type + "\""); 22175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return newFormat; 22195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final Locale rootLocale = new Locale(""); // Locale.ROOT only @since 1.6 22225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int findKeyword(String s, String[] list) { 22245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath s = PatternProps.trimWhiteSpace(s).toLowerCase(rootLocale); 22255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int i = 0; i < list.length; ++i) { 22265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (s.equals(list[i])) 22275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return i; 22285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return -1; 22305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void cacheExplicitFormats() { 22335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (cachedFormatters != null) { 22345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath cachedFormatters.clear(); 22355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath customFormatArgStarts = null; 22375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT 22385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // which we need not examine. 22395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limit = msgPattern.countParts() - 2; 22405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // This loop starts at part index 1 because we do need to examine 22415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // ARG_START parts. (But we can ignore the MSG_START.) 22425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=1; i < limit; ++i) { 22435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part = msgPattern.getPart(i); 22445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(part.getType()!=Part.Type.ARG_START) { 22455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; 22465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArgType argType=part.getArgType(); 22485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType != ArgType.SIMPLE) { 22495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; 22505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index = i; 22525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath i += 2; 22535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String explicitType = msgPattern.getSubstring(msgPattern.getPart(i++)); 22545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String style = ""; 22555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if ((part = msgPattern.getPart(i)).getType() == MessagePattern.Part.Type.ARG_STYLE) { 22565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath style = msgPattern.getSubstring(part); 22575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++i; 22585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Format formatter = createAppropriateFormat(explicitType, style); 22605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setArgStartFormat(index, formatter); 22615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 22655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets a formatter for a MessagePattern ARG_START part index. 22665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 22675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void setArgStartFormat(int argStart, Format formatter) { 22685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (cachedFormatters == null) { 22695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath cachedFormatters = new HashMap<Integer, Format>(); 22705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath cachedFormatters.put(argStart, formatter); 22725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 22755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Sets a custom formatter for a MessagePattern ARG_START part index. 22765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * "Custom" formatters are provided by the user via setFormat() or similar APIs. 22775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 22785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void setCustomArgStartFormat(int argStart, Format formatter) { 22795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath setArgStartFormat(argStart, formatter); 22805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (customFormatArgStarts == null) { 22815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath customFormatArgStarts = new HashSet<Integer>(); 22825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath customFormatArgStarts.add(argStart); 22845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 22855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final char SINGLE_QUOTE = '\''; 22875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final char CURLY_BRACE_LEFT = '{'; 22885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final char CURLY_BRACE_RIGHT = '}'; 22895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int STATE_INITIAL = 0; 22915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int STATE_SINGLE_QUOTE = 1; 22925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int STATE_IN_QUOTE = 2; 22935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int STATE_MSG_ELEMENT = 3; 22945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 22955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 22965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@icu} Converts an 'apostrophe-friendly' pattern into a standard 22975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pattern. 22985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <em>This is obsolete for ICU 4.8 and higher MessageFormat pattern strings.</em> 22995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * It can still be useful together with the JDK MessageFormat. 23005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 23015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>See the class description for more about apostrophes and quoting, 23025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and differences between ICU and the JDK. 23035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 23045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p>The JDK MessageFormat and ICU 4.6 and earlier MessageFormat 23055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * treat all ASCII apostrophes as 23065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * quotes, which is problematic in some languages, e.g. 23075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * French, where apostrophe is commonly used. This utility 23085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * assumes that only an unpaired apostrophe immediately before 23095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a brace is a true quote. Other unpaired apostrophes are paired, 23105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and the resulting standard pattern string is returned. 23115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 23125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p><b>Note</b>: It is not guaranteed that the returned pattern 23135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is indeed a valid pattern. The only effect is to convert 23145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * between patterns having different quoting semantics. 23155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 23165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p><b>Note</b>: This method only works on top-level messageText, 23175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * not messageText nested inside a complexArg. 23185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 23195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern the 'apostrophe-friendly' pattern to convert 23205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the standard equivalent of the original pattern 23215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 3.4 23225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 23235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static String autoQuoteApostrophe(String pattern) { 23245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder buf = new StringBuilder(pattern.length() * 2); 23255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int state = STATE_INITIAL; 23265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int braceCount = 0; 23275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (int i = 0, j = pattern.length(); i < j; ++i) { 23285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c = pattern.charAt(i); 23295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (state) { 23305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case STATE_INITIAL: 23315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (c) { 23325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case SINGLE_QUOTE: 23335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_SINGLE_QUOTE; 23345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case CURLY_BRACE_LEFT: 23365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_MSG_ELEMENT; 23375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++braceCount; 23385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case STATE_SINGLE_QUOTE: 23425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (c) { 23435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case SINGLE_QUOTE: 23445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_INITIAL; 23455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case CURLY_BRACE_LEFT: 23475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case CURLY_BRACE_RIGHT: 23485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_IN_QUOTE; 23495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: 23515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath buf.append(SINGLE_QUOTE); 23525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_INITIAL; 23535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case STATE_IN_QUOTE: 23575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (c) { 23585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case SINGLE_QUOTE: 23595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_INITIAL; 23605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case STATE_MSG_ELEMENT: 23645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath switch (c) { 23655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case CURLY_BRACE_LEFT: 23665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++braceCount; 23675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath case CURLY_BRACE_RIGHT: 23695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (--braceCount == 0) { 23705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath state = STATE_INITIAL; 23715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ///CLOVER:OFF 23765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath default: // Never happens. 23775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 23785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ///CLOVER:ON 23795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath buf.append(c); 23815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // End of scan 23835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) { 23845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath buf.append(SINGLE_QUOTE); 23855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return new String(buf); 23875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 23885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 23895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 23905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Convenience wrapper for Appendable, tracks the result string length. 23915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Also, Appendable throws IOException, and we turn that into a RuntimeException 23925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * so that we need no throws clauses. 23935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 23945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final class AppendableWrapper { 23955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public AppendableWrapper(StringBuilder sb) { 23965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath app = sb; 23975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length = sb.length(); 23985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath attributes = null; 23995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public AppendableWrapper(StringBuffer sb) { 24025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath app = sb; 24035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length = sb.length(); 24045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath attributes = null; 24055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void useAttributes() { 24085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath attributes = new ArrayList<AttributeAndPosition>(); 24095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void append(CharSequence s) { 24125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 24135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath app.append(s); 24145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length += s.length(); 24155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } catch(IOException e) { 24168d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath throw new ICUUncheckedIOException(e); 24175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void append(CharSequence s, int start, int limit) { 24215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 24225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath app.append(s, start, limit); 24235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length += limit - start; 24245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } catch(IOException e) { 24258d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath throw new ICUUncheckedIOException(e); 24265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void append(CharacterIterator iterator) { 24305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length += append(app, iterator); 24315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static int append(Appendable result, CharacterIterator iterator) { 24345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 24355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start = iterator.getBeginIndex(); 24365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limit = iterator.getEndIndex(); 24375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length = limit - start; 24385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (start < limit) { 24395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath result.append(iterator.first()); 24405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while (++start < limit) { 24415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath result.append(iterator.next()); 24425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return length; 24455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } catch(IOException e) { 24468d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath throw new ICUUncheckedIOException(e); 24475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void formatAndAppend(Format formatter, Object arg) { 24515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (attributes == null) { 24525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath append(formatter.format(arg)); 24535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 24545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath AttributedCharacterIterator formattedArg = formatter.formatToCharacterIterator(arg); 24555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int prevLength = length; 24565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath append(formattedArg); 24575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Copy all of the attributes from formattedArg to our attributes list. 24585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath formattedArg.first(); 24595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start = formattedArg.getIndex(); // Should be 0 but might not be. 24605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limit = formattedArg.getEndIndex(); // == start + length - prevLength 24615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int offset = prevLength - start; // Adjust attribute indexes for the result string. 24625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while (start < limit) { 24635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Map<Attribute, Object> map = formattedArg.getAttributes(); 24645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int runLimit = formattedArg.getRunLimit(); 24655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (map.size() != 0) { 24665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for (Map.Entry<Attribute, Object> entry : map.entrySet()) { 24675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath attributes.add( 24685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath new AttributeAndPosition( 24695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath entry.getKey(), entry.getValue(), 24705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath offset + start, offset + runLimit)); 24715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath start = runLimit; 24745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath formattedArg.setIndex(start); 24755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void formatAndAppend(Format formatter, Object arg, String argString) { 24805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if (attributes == null && argString != null) { 24815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath append(argString); 24825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 24835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath formatAndAppend(formatter, arg); 24845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private Appendable app; 24885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int length; 24895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private List<AttributeAndPosition> attributes; 24905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 24925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final class AttributeAndPosition { 24935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 24945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Defaults the field to Field.ARGUMENT. 24955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 24965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public AttributeAndPosition(Object fieldValue, int startIndex, int limitIndex) { 24975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath init(Field.ARGUMENT, fieldValue, startIndex, limitIndex); 24985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 24995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 25005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public AttributeAndPosition(Attribute field, Object fieldValue, int startIndex, int limitIndex) { 25015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath init(field, fieldValue, startIndex, limitIndex); 25025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 25035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 25045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void init(Attribute field, Object fieldValue, int startIndex, int limitIndex) { 25055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath key = field; 25065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath value = fieldValue; 25075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath start = startIndex; 25085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath limit = limitIndex; 25095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 25105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 25115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private Attribute key; 25125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private Object value; 25135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int start; 25145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int limit; 25155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 25165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath} 2517