15d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath/* 25d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath******************************************************************************* 38d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath* Copyright (C) 2010-2014, International Business Machines 45d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* Corporation and others. All Rights Reserved. 55d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath******************************************************************************* 65d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* created on: 2010aug21 75d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath* created by: Markus W. Scherer 85d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath*/ 95d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathpackage com.ibm.icu.text; 115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.ArrayList; 135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport java.util.Locale; 145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.impl.ICUConfig; 165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.impl.PatternProps; 175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathimport com.ibm.icu.util.Freezable; 188d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamathimport com.ibm.icu.util.ICUCloneNotSupportedException; 195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath//Note: Minimize ICU dependencies, only use a very small part of the ICU core. 215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath//In particular, do not depend on *Format classes. 225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath/** 245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses and represents ICU MessageFormat patterns. 255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Also handles patterns for ChoiceFormat, PluralFormat and SelectFormat. 265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Used in the implementations of those classes as well as in tools 275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for message validation, translation and format conversion. 285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The parser handles all syntax relevant for identifying message arguments. 305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This includes "complex" arguments whose style strings contain 315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * nested MessageFormat pattern substrings. 325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For "simple" arguments (with no nested MessageFormat pattern substrings), 335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the argument style is not parsed any further. 345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The parser handles named and numbered message arguments and allows both in one message. 365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Once a pattern has been parsed successfully, iterate through the parsed data 385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with countParts(), getPart() and related methods. 395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The data logically represents a parse tree, but is stored and accessed 415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as a list of "parts" for fast and simple parsing and to minimize object allocations. 425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Arguments and nested messages are best handled via recursion. 435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For every _START "part", {@link #getLimitPartIndex(int)} efficiently returns 445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the index of the corresponding _LIMIT "part". 455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * List of "parts": 475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <pre> 485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * message = MSG_START (SKIP_SYNTAX | INSERT_CHAR | REPLACE_NUMBER | argument)* MSG_LIMIT 495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * argument = noneArg | simpleArg | complexArg 505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * complexArg = choiceArg | pluralArg | selectArg 515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * noneArg = ARG_START.NONE (ARG_NAME | ARG_NUMBER) ARG_LIMIT.NONE 535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * simpleArg = ARG_START.SIMPLE (ARG_NAME | ARG_NUMBER) ARG_TYPE [ARG_STYLE] ARG_LIMIT.SIMPLE 545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * choiceArg = ARG_START.CHOICE (ARG_NAME | ARG_NUMBER) choiceStyle ARG_LIMIT.CHOICE 555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pluralArg = ARG_START.PLURAL (ARG_NAME | ARG_NUMBER) pluralStyle ARG_LIMIT.PLURAL 565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * selectArg = ARG_START.SELECT (ARG_NAME | ARG_NUMBER) selectStyle ARG_LIMIT.SELECT 575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * choiceStyle = ((ARG_INT | ARG_DOUBLE) ARG_SELECTOR message)+ 595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * pluralStyle = [ARG_INT | ARG_DOUBLE] (ARG_SELECTOR [ARG_INT | ARG_DOUBLE] message)+ 605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * selectStyle = (ARG_SELECTOR message)+ 615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </pre> 625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <ul> 635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>Literal output text is not represented directly by "parts" but accessed 645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * between parts of a message, from one part's getLimit() to the next part's getIndex(). 655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li><code>ARG_START.CHOICE</code> stands for an ARG_START Part with ArgType CHOICE. 665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>In the choiceStyle, the ARG_SELECTOR has the '<', the '#' or 675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the less-than-or-equal-to sign (U+2264). 685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>In the pluralStyle, the first, optional numeric Part has the "offset:" value. 695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The optional numeric Part between each (ARG_SELECTOR, message) pair 705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is the value of an explicit-number selector like "=2", 715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * otherwise the selector is a non-numeric identifier. 725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <li>The REPLACE_NUMBER Part can occur only in an immediate sub-message of the pluralStyle. 735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This class is not intended for public subclassing. 755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @author Markus Scherer 785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamathpublic final class MessagePattern implements Cloneable, Freezable<MessagePattern> { 805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Mode for when an apostrophe starts quoted literal text for MessageFormat output. 825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The default is DOUBLE_OPTIONAL unless overridden via ICUConfig 835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (/com/ibm/icu/ICUConfig.properties). 845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A pair of adjacent apostrophes always results in a single apostrophe in the output, 865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * even when the pair is between two single, text-quoting apostrophes. 875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The following table shows examples of desired MessageFormat.format() output 895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with the pattern strings that yield that output. 905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <table> 925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>Desired output</th> 945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>DOUBLE_OPTIONAL</th> 955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <th>DOUBLE_REQUIRED</th> 965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </tr> 975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I see {many}</td> 995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I see '{many}'</td> 1005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>(same)</td> 1015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </tr> 1025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I said {'Wow!'}</td> 1045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I said '{''Wow!''}'</td> 1055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>(same)</td> 1065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </tr> 1075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <tr> 1085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I don't know</td> 1095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I don't know OR<br> I don''t know</td> 1105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <td>I don''t know</td> 1115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </tr> 1125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * </table> 1135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public enum ApostropheMode { 1165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A literal apostrophe is represented by 1185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * either a single or a double apostrophe pattern character. 1195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Within a MessageFormat pattern, a single apostrophe only starts quoted literal text 1205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * if it immediately precedes a curly brace {}, 1215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * or a pipe symbol | if inside a choice format, 1225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * or a pound symbol # if inside a plural format. 1235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 1245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is the default behavior starting with ICU 4.8. 1255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DOUBLE_OPTIONAL, 1285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A literal apostrophe must be represented by 1305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * a double apostrophe pattern character. 1315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A single apostrophe always starts quoted literal text. 1325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 1335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is the behavior of ICU 4.6 and earlier, and of the JDK. 1345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath DOUBLE_REQUIRED 1375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 1385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 1395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constructs an empty MessagePattern with default ApostropheMode. 1415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern() { 1445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode=defaultAposMode; 1455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 1465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 1475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constructs an empty MessagePattern. 1495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param mode Explicit ApostropheMode. 1505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern(ApostropheMode mode) { 1535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode=mode; 1545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 1555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 1565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Constructs a MessagePattern with default ApostropheMode and 1585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * parses the MessageFormat pattern string. 1595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern a MessageFormat pattern string 1605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException for syntax errors in the pattern string 1615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if certain limits are exceeded 1625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., argument number too high, argument name too long, etc.) 1635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NumberFormatException if a number could not be parsed 1645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern(String pattern) { 1675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode=defaultAposMode; 1685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parse(pattern); 1695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 1705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 1715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses a MessageFormat pattern string. 1735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern a MessageFormat pattern string 1745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this 1755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException for syntax errors in the pattern string 1765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if certain limits are exceeded 1775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., argument number too high, argument name too long, etc.) 1785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NumberFormatException if a number could not be parsed 1795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern parse(String pattern) { 1825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath preParse(pattern); 1835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parseMessage(0, 0, 0, ArgType.NONE); 1845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath postParse(); 1855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 1865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 1875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 1885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 1895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses a ChoiceFormat pattern string. 1905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern a ChoiceFormat pattern string 1915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this 1925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException for syntax errors in the pattern string 1935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if certain limits are exceeded 1945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., argument number too high, argument name too long, etc.) 1955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NumberFormatException if a number could not be parsed 1965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 1975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 1985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern parseChoiceStyle(String pattern) { 1995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath preParse(pattern); 2005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parseChoiceStyle(0, 0); 2015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath postParse(); 2025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 2035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses a PluralFormat pattern string. 2075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern a PluralFormat pattern string 2085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this 2095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException for syntax errors in the pattern string 2105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if certain limits are exceeded 2115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., argument number too high, argument name too long, etc.) 2125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NumberFormatException if a number could not be parsed 2135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern parsePluralStyle(String pattern) { 2165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath preParse(pattern); 2175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parsePluralOrSelectStyle(ArgType.PLURAL, 0, 0); 2185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath postParse(); 2195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 2205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses a SelectFormat pattern string. 2245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pattern a SelectFormat pattern string 2255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this 2265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IllegalArgumentException for syntax errors in the pattern string 2275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if certain limits are exceeded 2285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., argument number too high, argument name too long, etc.) 2295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws NumberFormatException if a number could not be parsed 2305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern parseSelectStyle(String pattern) { 2335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath preParse(pattern); 2345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parsePluralOrSelectStyle(ArgType.SELECT, 0, 0); 2355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath postParse(); 2365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 2375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Clears this MessagePattern. 2415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * countParts() will return 0. 2425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void clear() { 2455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Mostly the same as preParse(). 2465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(isFrozen()) { 2475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new UnsupportedOperationException( 2485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Attempt to clear() a frozen MessagePattern instance."); 2495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msg=null; 2515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath hasArgNames=hasArgNumbers=false; 2525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath needsAutoQuoting=false; 2535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.clear(); 2545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(numericValues!=null) { 2555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericValues.clear(); 2565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Clears this MessagePattern and sets the ApostropheMode. 2615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * countParts() will return 0. 2625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param mode The new ApostropheMode. 2635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public void clearPatternAndSetApostropheMode(ApostropheMode mode) { 2665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath clear(); 2675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode=mode; 2685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param other another object to compare with. 2725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if this object is equivalent to the other one. 2735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 2765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean equals(Object other) { 2775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(this==other) { 2785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return true; 2795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(other==null || getClass()!=other.getClass()) { 2815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return false; 2825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern o=(MessagePattern)other; 2845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 2855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode.equals(o.aposMode) && 2865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (msg==null ? o.msg==null : msg.equals(o.msg)) && 2875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.equals(o.parts); 2885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // No need to compare numericValues if msg and parts are the same. 2895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 2915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 2925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@inheritDoc} 2935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 2945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 2955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 2965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int hashCode() { 2975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return (aposMode.hashCode()*37+(msg!=null ? msg.hashCode() : 0))*37+parts.hashCode(); 2985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 2995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this instance's ApostropheMode. 3025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public ApostropheMode getApostropheMode() { 3055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return aposMode; 3065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if getApostropheMode() == ApostropheMode.DOUBLE_REQUIRED 3105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @internal 3115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3128d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath public boolean jdkAposMode() { 3135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return aposMode == ApostropheMode.DOUBLE_REQUIRED; 3145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the parsed pattern string (null if none was parsed). 3185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String getPatternString() { 3215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg; 3225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Does the parsed pattern have named arguments like {first_name}? 3265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if the parsed pattern has at least one named argument. 3275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean hasNamedArguments() { 3305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return hasArgNames; 3315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Does the parsed pattern have numbered arguments like {2}? 3355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if the parsed pattern has at least one numbered argument. 3365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean hasNumberedArguments() { 3395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return hasArgNumbers; 3405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@inheritDoc} 3445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 3475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String toString() { 3485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg; 3495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Validates and parses an argument name or argument number string. 3535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * An argument name must be a "pattern identifier", that is, it must contain 3545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * no Unicode Pattern_Syntax or Pattern_White_Space characters. 3555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If it only contains ASCII digits, then it must be a small integer with no leading zero. 3565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param name Input string. 3575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return >=0 if the name is a valid number, 3585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, 3595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ARG_NAME_NOT_VALID (-2) if it is neither. 3605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static int validateArgumentName(String name) { 3635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!PatternProps.isIdentifier(name)) { 3645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARG_NAME_NOT_VALID; 3655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parseArgNumber(name, 0, name.length()); 3675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Return value from {@link #validateArgumentName(String)} for when 3715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the string is a valid "pattern identifier" but not a number. 3725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static final int ARG_NAME_NOT_NUMBER=-1; 3755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Return value from {@link #validateArgumentName(String)} for when 3785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the string is invalid. 3795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * It might not be a valid "pattern identifier", 3805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * or it have only ASCII digits but there is a leading zero or the number is too large. 3815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static final int ARG_NAME_NOT_VALID=-2; 3845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 3855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 3865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns a version of the parsed pattern string where each ASCII apostrophe 3875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * is doubled (escaped) if it is not already, and if it is not interpreted as quoting syntax. 3885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 3895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For example, this turns "I don't '{know}' {gender,select,female{h''er}other{h'im}}." 3905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * into "I don''t '{know}' {gender,select,female{h''er}other{h''im}}." 3915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the deep-auto-quoted version of the parsed pattern string. 3925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see MessageFormat#autoQuoteApostrophe(String) 3935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 3945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 3955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String autoQuoteApostropheDeep() { 3965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!needsAutoQuoting) { 3975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg; 3985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 3995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder modified=null; 4005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Iterate backward so that the insertion indexes do not change. 4015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int count=countParts(); 4025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(int i=count; i>0;) { 4035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part; 4045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if((part=getPart(--i)).getType()==Part.Type.INSERT_CHAR) { 4055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(modified==null) { 4065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath modified=new StringBuilder(msg.length()+10).append(msg); 4075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath modified.insert(part.index, (char)part.value); 4095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(modified==null) { 4125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg; 4135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 4145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return modified.toString(); 4155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the number of "parts" created by parsing the pattern string. 4205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns 0 if no pattern has been parsed or clear() was called. 4215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the number of pattern parts. 4225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int countParts() { 4255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parts.size(); 4265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Gets the i-th pattern "part". 4305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param i The index of the Part data. (0..countParts()-1) 4315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the i-th pattern "part". 4325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if i is outside the (0..countParts()-1) range 4335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Part getPart(int i) { 4365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parts.get(i); 4375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the Part.Type of the i-th pattern "part". 4415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Convenience method for getPart(i).getType(). 4425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param i The index of the Part data. (0..countParts()-1) 4435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return The Part.Type of the i-th Part. 4445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if i is outside the (0..countParts()-1) range 4455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Part.Type getPartType(int i) { 4485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parts.get(i).type; 4495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the pattern index of the specified pattern "part". 4535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Convenience method for getPart(partIndex).getIndex(). 4545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param partIndex The index of the Part data. (0..countParts()-1) 4555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return The pattern index of this Part. 4565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if partIndex is outside the (0..countParts()-1) range 4575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getPatternIndex(int partIndex) { 4605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parts.get(partIndex).index; 4615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the substring of the pattern string indicated by the Part. 4655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Convenience method for getPatternString().substring(part.getIndex(), part.getLimit()). 4665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param part a part of this MessagePattern. 4675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the substring associated with part. 4685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String getSubstring(Part part) { 4715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index=part.index; 4725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg.substring(index, index+part.length); 4735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Compares the part's substring with the input string s. 4775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param part a part of this MessagePattern. 4785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param s a string. 4795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if getSubstring(part).equals(s). 4805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean partSubstringMatches(Part part, String s) { 4835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return msg.regionMatches(part.index, s, 0, part.length); 4845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 4855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 4865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 4875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the numeric value associated with an ARG_INT or ARG_DOUBLE. 4885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param part a part of this MessagePattern. 4895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the part's numeric value, or NO_NUMERIC_VALUE if this is not a numeric part. 4905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 4915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 4925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public double getNumericValue(Part part) { 4935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part.Type type=part.type; 4945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Part.Type.ARG_INT) { 4955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return part.value; 4965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(type==Part.Type.ARG_DOUBLE) { 4975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return numericValues.get(part.value); 4985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 4995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return NO_NUMERIC_VALUE; 5005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Special value that is returned by getNumericValue(Part) when no 5055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * numeric value is defined for a part. 5065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see #getNumericValue 5075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static final double NO_NUMERIC_VALUE=-123456789; 5105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified. 5135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1) 5145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the "offset:" value. 5155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if pluralStart is outside the (0..countParts()-1) range 5165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public double getPluralOffset(int pluralStart) { 5195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part part=parts.get(pluralStart); 5205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(part.type.hasNumericValue()) { 5215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return getNumericValue(part); 5225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 5235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 0; 5245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the index of the ARG|MSG_LIMIT part corresponding to the ARG|MSG_START at start. 5295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param start The index of some Part data (0..countParts()-1); 5305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * this Part should be of Type ARG_START or MSG_START. 5315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return The first i>start where getPart(i).getType()==ARG|MSG_LIMIT at the same nesting level, 5325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * or start itself if getPartType(msgStart)!=ARG|MSG_START. 5335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @throws IndexOutOfBoundsException if start is outside the (0..countParts()-1) range 5345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getLimitPartIndex(int start) { 5375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limit=parts.get(start).limitPartIndex; 5385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(limit<start) { 5395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return start; 5405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return limit; 5425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A message pattern "part", representing a pattern parsing event. 5465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * There is a part for the start and end of a message or argument, 5475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for quoting and escaping of and with ASCII apostrophes, 5485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and for syntax elements of "complex" arguments. 5495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public static final class Part { 5525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private Part(Type t, int i, int l, int v) { 5535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath type=t; 5545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=i; 5555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length=(char)l; 5565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath value=(short)v; 5575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the type of this part. 5615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the part type. 5625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Type getType() { 5655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return type; 5665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the pattern string index associated with this Part. 5705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this part's pattern string index. 5715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getIndex() { 5745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 5755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the length of the pattern substring associated with this Part. 5795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This is 0 for some parts. 5805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this part's pattern substring length. 5815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getLength() { 5845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return length; 5855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the pattern string limit (exclusive-end) index associated with this Part. 5895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Convenience method for getIndex()+getLength(). 5905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this part's pattern string limit index, same as getIndex()+getLength(). 5915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 5925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 5935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getLimit() { 5945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index+length; 5955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 5965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 5975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 5985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns a value associated with this part. 5995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * See the documentation of each part type for details. 6005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the part value. 6015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int getValue() { 6045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return value; 6055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 6075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns the argument type if this part is of type ARG_START or ARG_LIMIT, 6095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * otherwise ArgType.NONE. 6105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return the argument type for this part. 6115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public ArgType getArgType() { 6145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Type type=getType(); 6155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(type==Type.ARG_START || type==Type.ARG_LIMIT) { 6165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return argTypes[value]; 6175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 6185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ArgType.NONE; 6195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 6215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 6225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Part type constants. 6245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public enum Type { 6275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Start of a message pattern (main or nested). 6295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The length is 0 for the top-level message 6305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and for a choice argument sub-message, otherwise 1 for the '{'. 6315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value indicates the nesting level, starting with 0 for the main message. 6325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * There is always a later MSG_LIMIT part. 6345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MSG_START, 6375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * End of a message pattern (main or nested). 6395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The length is 0 for the top-level message and 6405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * the last sub-message of a choice argument, 6415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * otherwise 1 for the '}' or (in a choice argument style) the '|'. 6425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value indicates the nesting level, starting with 0 for the main message. 6435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MSG_LIMIT, 6465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Indicates a substring of the pattern string which is to be skipped when formatting. 6485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * For example, an apostrophe that begins or ends quoted text 6495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * would be indicated with such a part. 6505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 6515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath SKIP_SYNTAX, 6545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Indicates that a syntax character needs to be inserted for auto-quoting. 6565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The length is 0. 6575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is the character code of the insertion character. (U+0027=APOSTROPHE) 6585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath INSERT_CHAR, 6615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Indicates a syntactic (non-escaped) # symbol in a plural variant. 6635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * When formatting, replace this part's substring with the 6645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (value-offset) for the plural argument value. 6655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 6665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath REPLACE_NUMBER, 6695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Start of an argument. 6715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The length is 1 for the '{'. 6725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is the ordinal value of the ArgType. Use getArgType(). 6735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * <p> 6745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This part is followed by either an ARG_NUMBER or ARG_NAME, 6755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * followed by optional argument sub-parts (see ArgType constants) 6765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and finally an ARG_LIMIT part. 6775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_START, 6805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * End of an argument. 6825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The length is 1 for the '}'. 6835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is the ordinal value of the ArgType. Use getArgType(). 6845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_LIMIT, 6875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument number, provided by the value. 6895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_NUMBER, 6925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument name. 6945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 6955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 6965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 6975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_NAME, 6985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 6995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument type. 7005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 7015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_TYPE, 7045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument style text. 7065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 7075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_STYLE, 7105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A selector substring in a "complex" argument style. 7125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The value is undefined and currently always 0. 7135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_SELECTOR, 7165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * An integer value, for example the offset or an explicit selector value 7185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in a PluralFormat style. 7195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The part value is the integer value. 7205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_INT, 7235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * A numeric value, for example the offset or an explicit selector value 7255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * in a PluralFormat style. 7265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The part value is an index into an internal array of numeric values; 7275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * use getNumericValue(). 7285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ARG_DOUBLE; 7315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Indicates whether this part has a numeric value. 7345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If so, then that numeric value can be retrieved via {@link MessagePattern#getNumericValue(Part)}. 7355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if this part has a numeric value. 7365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean hasNumericValue() { 7395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this==ARG_INT || this==ARG_DOUBLE; 7405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return a string representation of this part. 7455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 7485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public String toString() { 7495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath String valueString=(type==Type.ARG_START || type==Type.ARG_LIMIT) ? 7505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath getArgType().name() : Integer.toString(value); 7515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return type.name()+"("+valueString+")@"+index; 7525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param other another object to compare with. 7565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if this object is equivalent to the other one. 7575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 7605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean equals(Object other) { 7615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(this==other) { 7625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return true; 7635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(other==null || getClass()!=other.getClass()) { 7655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return false; 7665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath Part o=(Part)other; 7685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 7695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath type.equals(o.type) && 7705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index==o.index && 7715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath length==o.length && 7725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath value==o.value && 7735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath limitPartIndex==o.limitPartIndex; 7745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * {@inheritDoc} 7785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 7795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 7805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 7815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public int hashCode() { 7825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ((type.hashCode()*37+index)*37+length)*37+value; 7835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int MAX_LENGTH=0xffff; 7865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int MAX_VALUE=Short.MAX_VALUE; 7875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Some fields are not final because they are modified during pattern parsing. 7895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // After pattern parsing, the parts are effectively immutable. 7905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private final Type type; 7915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private final int index; 7925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private final char length; 7935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private short value; 7945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int limitPartIndex; 7955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 7965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 7975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 7985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Argument type constants. 7995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returned by Part.getArgType() for ARG_START and ARG_LIMIT parts. 8005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * 8015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Messages nested inside an argument are each delimited by MSG_START and MSG_LIMIT, 8025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with a nesting level one greater than the surrounding message. 8035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public enum ArgType { 8065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument has no specified type. 8085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath NONE, 8115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument has a "simple" type which is provided by the ARG_TYPE part. 8135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * An ARG_STYLE part might follow that. 8145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath SIMPLE, 8175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument is a ChoiceFormat with one or more 8195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ((ARG_INT | ARG_DOUBLE), ARG_SELECTOR, message) tuples. 8205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath CHOICE, 8235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument is a cardinal-number PluralFormat with an optional ARG_INT or ARG_DOUBLE offset 8255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * (e.g., offset:1) 8265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * and one or more (ARG_SELECTOR [explicit-value] message) tuples. 8275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * If the selector has an explicit value (e.g., =2), then 8285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * that value is provided by the ARG_INT or ARG_DOUBLE part preceding the message. 8295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Otherwise the message immediately follows the ARG_SELECTOR. 8305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath PLURAL, 8335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument is a SelectFormat with one or more (ARG_SELECTOR, message) pairs. 8355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath SELECT, 8385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * The argument is an ordinal-number PluralFormat 8405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * with the same style parts sequence and semantics as {@link ArgType#PLURAL}. 8415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 50 8425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath SELECTORDINAL; 8445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if the argument type has a plural style part sequence and semantics, 8475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * for example {@link ArgType#PLURAL} and {@link ArgType#SELECTORDINAL}. 8485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 50 8495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean hasPluralStyle() { 8515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this == PLURAL || this == SELECTORDINAL; 8525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Creates and returns a copy of this object. 8575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return a copy of this object (or itself if frozen). 8585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @Override 8615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public Object clone() { 8625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(isFrozen()) { 8635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 8645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 8655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return cloneAsThawed(); 8665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Creates and returns an unfrozen copy of this object. 8715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return a copy of this object. 8725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath @SuppressWarnings("unchecked") 8755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern cloneAsThawed() { 8765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath MessagePattern newMsg; 8775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath try { 8785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newMsg=(MessagePattern)super.clone(); 8795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } catch (CloneNotSupportedException e) { 8808d05787d6a4b5762d790ccd2a9ed9dc8885986efNarayan Kamath throw new ICUCloneNotSupportedException(e); 8815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newMsg.parts=(ArrayList<Part>)parts.clone(); 8835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(numericValues!=null) { 8845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newMsg.numericValues=(ArrayList<Double>)numericValues.clone(); 8855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath newMsg.frozen=false; 8875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return newMsg; 8885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 8905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 8915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Freezes this object, making it immutable and thread-safe. 8925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return this 8935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 8945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 8955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public MessagePattern freeze() { 8965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath frozen=true; 8975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return this; 8985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 8995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 9015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Determines whether this object is frozen (immutable) or not. 9025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if this object is frozen. 9035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @stable ICU 4.8 9045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 9055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath public boolean isFrozen() { 9065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return frozen; 9075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void preParse(String pattern) { 9105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(isFrozen()) { 9115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new UnsupportedOperationException( 9125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Attempt to parse("+prefix(pattern)+") on frozen MessagePattern instance."); 9135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msg=pattern; 9155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath hasArgNames=hasArgNumbers=false; 9165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath needsAutoQuoting=false; 9175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.clear(); 9185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(numericValues!=null) { 9195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericValues.clear(); 9205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void postParse() { 9245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Nothing to be done currently. 9255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 9275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parseMessage(int index, int msgStartLength, int nestingLevel, ArgType parentType) { 9285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(nestingLevel>Part.MAX_VALUE) { 9295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException(); 9305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int msgStart=parts.size(); 9325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.MSG_START, index, msgStartLength, nestingLevel); 9335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index+=msgStartLength; 9345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while(index<msg.length()) { 9355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index++); 9365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='\'') { 9375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 9385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The apostrophe is the last character in the pattern. 9395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Add a Part for auto-quoting. 9405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.INSERT_CHAR, index, 0, '\''); // value=char to be inserted 9415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath needsAutoQuoting=true; 9425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 9435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=msg.charAt(index); 9445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='\'') { 9455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // double apostrophe, skip the second one 9465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.SKIP_SYNTAX, index++, 1, 0); 9475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if( 9485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath aposMode==ApostropheMode.DOUBLE_REQUIRED || 9495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=='{' || c=='}' || 9505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (parentType==ArgType.CHOICE && c=='|') || 9515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath (parentType.hasPluralStyle() && c=='#') 9525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ) { 9535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // skip the quote-starting apostrophe 9545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.SKIP_SYNTAX, index-1, 1, 0); 9555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // find the end of the quoted literal text 9565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 9575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=msg.indexOf('\'', index+1); 9585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index>=0) { 9595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if((index+1)<msg.length() && msg.charAt(index+1)=='\'') { 9605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // double apostrophe inside quoted literal text 9615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // still encodes a single apostrophe, skip the second one 9625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.SKIP_SYNTAX, ++index, 1, 0); 9635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 9645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // skip the quote-ending apostrophe 9655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.SKIP_SYNTAX, index++, 1, 0); 9665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 9675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 9695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The quoted text reaches to the end of the of the message. 9705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=msg.length(); 9715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Add a Part for auto-quoting. 9725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.INSERT_CHAR, index, 0, '\''); // value=char to be inserted 9735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath needsAutoQuoting=true; 9745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 9755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 9785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Interpret the apostrophe as literal text. 9795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Add a Part for auto-quoting. 9805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.INSERT_CHAR, index, 0, '\''); // value=char to be inserted 9815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath needsAutoQuoting=true; 9825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 9845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(parentType.hasPluralStyle() && c=='#') { 9855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The unquoted # in a plural message fragment will be replaced 9865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // with the (number-offset). 9875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.REPLACE_NUMBER, index-1, 1, 0); 9885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(c=='{') { 9895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parseArg(index-1, 1, nestingLevel); 9905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if((nestingLevel>0 && c=='}') || (parentType==ArgType.CHOICE && c=='|')) { 9915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Finish the message before the terminator. 9925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // In a choice style, report the "}" substring only for the following ARG_LIMIT, 9935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // not for this MSG_LIMIT. 9945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limitLength=(parentType==ArgType.CHOICE && c=='}') ? 0 : 1; 9955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addLimitPart(msgStart, Part.Type.MSG_LIMIT, index-1, limitLength, nestingLevel); 9965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(parentType==ArgType.CHOICE) { 9975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Let the choice style parser see the '}' or '|'. 9985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index-1; 9995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 10005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // continue parsing after the '}' 10015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 10025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } // else: c is part of literal text 10045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(nestingLevel>0 && !inTopLevelChoiceMessage(nestingLevel, parentType)) { 10065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 10075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unmatched '{' braces in message "+prefix()); 10085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addLimitPart(msgStart, Part.Type.MSG_LIMIT, index, 0, nestingLevel); 10105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 10115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 10135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parseArg(int index, int argStartLength, int nestingLevel) { 10145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int argStart=parts.size(); 10155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ArgType argType=ArgType.NONE; 10165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_START, index, argStartLength, argType.ordinal()); 10175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int nameIndex=index=skipWhiteSpace(index+argStartLength); 10185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 10195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 10205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unmatched '{' braces in message "+prefix()); 10215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // parse argument name or number 10235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipIdentifier(index); 10245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int number=parseArgNumber(nameIndex, index); 10255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(number>=0) { 10265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-nameIndex; 10275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH || number>Part.MAX_VALUE) { 10285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 10295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument number too large: "+prefix(nameIndex)); 10305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath hasArgNumbers=true; 10325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_NUMBER, nameIndex, length, number); 10335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(number==ARG_NAME_NOT_NUMBER) { 10345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-nameIndex; 10355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 10365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 10375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument name too long: "+prefix(nameIndex)); 10385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath hasArgNames=true; 10405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_NAME, nameIndex, length, 0); 10415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { // number<-1 (ARG_NAME_NOT_VALID) 10425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Bad argument syntax: "+prefix(nameIndex)); 10435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 10455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 10465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 10475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unmatched '{' braces in message "+prefix()); 10485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index); 10505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='}') { 10515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // all done 10525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(c!=',') { 10535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Bad argument syntax: "+prefix(nameIndex)); 10545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else /* ',' */ { 10555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // parse argument type: case-sensitive a-zA-Z 10565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int typeIndex=index=skipWhiteSpace(index+1); 10575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while(index<msg.length() && isArgTypeChar(msg.charAt(index))) { 10585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++index; 10595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-typeIndex; 10615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 10625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 10635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 10645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unmatched '{' braces in message "+prefix()); 10655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length==0 || ((c=msg.charAt(index))!=',' && c!='}')) { 10675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Bad argument syntax: "+prefix(nameIndex)); 10685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 10705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 10715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument type name too long: "+prefix(nameIndex)); 10725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType=ArgType.SIMPLE; 10745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length==6) { 10755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // case-insensitive comparisons for complex-type names 10765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(isChoice(typeIndex)) { 10775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType=ArgType.CHOICE; 10785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(isPlural(typeIndex)) { 10795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType=ArgType.PLURAL; 10805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(isSelect(typeIndex)) { 10815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType=ArgType.SELECT; 10825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(length==13) { 10845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(isSelect(typeIndex) && isOrdinal(typeIndex+6)) { 10855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType=ArgType.SELECTORDINAL; 10865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // change the ARG_START type from NONE to argType 10895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.get(argStart).value=(short)argType.ordinal(); 10905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType==ArgType.SIMPLE) { 10915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_TYPE, typeIndex, length, 0); 10925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // look for an argument style (pattern) 10945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='}') { 10955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType!=ArgType.SIMPLE) { 10965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 10975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "No style field for complex argument: "+prefix(nameIndex)); 10985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 10995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else /* ',' */ { 11005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++index; 11015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType==ArgType.SIMPLE) { 11025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parseSimpleStyle(index); 11035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(argType==ArgType.CHOICE) { 11045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parseChoiceStyle(index, nestingLevel); 11055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 11065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parsePluralOrSelectStyle(argType, index, nestingLevel); 11075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Argument parsing stopped on the '}'. 11115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addLimitPart(argStart, Part.Type.ARG_LIMIT, index, 1, argType.ordinal()); 11125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index+1; 11135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parseSimpleStyle(int index) { 11165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start=index; 11175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int nestedBraces=0; 11185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while(index<msg.length()) { 11195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index++); 11205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='\'') { 11215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Treat apostrophe as quoting but include it in the style part. 11225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Find the end of the quoted literal text. 11235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=msg.indexOf('\'', index); 11245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index<0) { 11255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Quoted literal argument style text reaches to the end of the message: "+ 11275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix(start)); 11285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // skip the quote-ending apostrophe 11305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++index; 11315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(c=='{') { 11325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++nestedBraces; 11335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(c=='}') { 11345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(nestedBraces>0) { 11355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath --nestedBraces; 11365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 11375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=--index-start; 11385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 11395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 11405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument style text too long: "+prefix(start)); 11415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_STYLE, start, length, 0); 11435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 11445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } // c is part of literal text 11465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Unmatched '{' braces in message "+prefix()); 11495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 11515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parseChoiceStyle(int index, int nestingLevel) { 11525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start=index; 11535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 11545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length() || msg.charAt(index)=='}') { 11555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Missing choice argument pattern in "+prefix()); 11575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 11595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // The choice argument style contains |-separated (number, separator, message) triples. 11605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Parse the number. 11615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int numberIndex=index; 11625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipDouble(index); 11635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-numberIndex; 11645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length==0) { 11655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Bad choice pattern syntax: "+prefix(start)); 11665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 11685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 11695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Choice number too long: "+prefix(numberIndex)); 11705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parseDouble(numberIndex, index, true); // adds ARG_INT or ARG_DOUBLE 11725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Parse the separator. 11735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 11745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 11755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException("Bad choice pattern syntax: "+prefix(start)); 11765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index); 11785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!(c=='#' || c=='<' || c=='\u2264')) { // U+2264 is <= 11795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Expected choice separator (#<\u2264) instead of '"+c+ 11815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "' in choice pattern "+prefix(start)); 11825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_SELECTOR, index, 1, 0); 11845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Parse the message fragment. 11855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parseMessage(++index, 0, nestingLevel+1, ArgType.CHOICE); 11865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // parseMessage(..., CHOICE) returns the index of the terminator, or msg.length(). 11875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length()) { 11885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 11895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(msg.charAt(index)=='}') { 11915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!inMessageFormatPattern(nestingLevel)) { 11925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 11935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Bad choice pattern syntax: "+prefix(start)); 11945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 11965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } // else the terminator is '|' 11975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index+1); 11985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 11995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 12015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parsePluralOrSelectStyle(ArgType argType, int index, int nestingLevel) { 12025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int start=index; 12035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean isEmpty=true; 12045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean hasOther=false; 12055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 12065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // First, collect the selector looking for a small set of terminators. 12075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // It would be a little faster to consider the syntax of each possible 12085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // token right here, but that makes the code too complicated. 12095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 12105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean eos=index==msg.length(); 12115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(eos || msg.charAt(index)=='}') { 12125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(eos==inMessageFormatPattern(nestingLevel)) { 12135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Bad "+ 12155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType.toString().toLowerCase(Locale.ENGLISH)+ 12165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath " pattern syntax: "+prefix(start)); 12175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!hasOther) { 12195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Missing 'other' keyword in "+ 12215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType.toString().toLowerCase(Locale.ENGLISH)+ 12225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath " pattern in "+prefix()); 12235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 12255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int selectorIndex=index; 12275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(argType.hasPluralStyle() && msg.charAt(selectorIndex)=='=') { 12285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // explicit-value plural selector: =double 12295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipDouble(index+1); 12305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-selectorIndex; 12315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length==1) { 12325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Bad "+ 12345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType.toString().toLowerCase(Locale.ENGLISH)+ 12355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath " pattern syntax: "+prefix(start)); 12365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 12385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 12395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument selector too long: "+prefix(selectorIndex)); 12405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_SELECTOR, selectorIndex, length, 0); 12425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parseDouble(selectorIndex+1, index, false); // adds ARG_INT or ARG_DOUBLE 12435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipIdentifier(index); 12455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int length=index-selectorIndex; 12465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length==0) { 12475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Bad "+ 12495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType.toString().toLowerCase(Locale.ENGLISH)+ 12505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath " pattern syntax: "+prefix(start)); 12515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Note: The ':' in "offset:" is just beyond the skipIdentifier() range. 12535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if( argType.hasPluralStyle() && length==6 && index<msg.length() && 12545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath msg.regionMatches(selectorIndex, "offset:", 0, 7) 12555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ) { 12565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // plural offset, not a selector 12575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(!isEmpty) { 12585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Plural argument 'offset:' (if present) must precede key-message pairs: "+ 12605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix(start)); 12615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // allow whitespace between offset: and its value 12635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int valueIndex=skipWhiteSpace(index+1); // The ':' is at index. 12645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipDouble(valueIndex); 12655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==valueIndex) { 12665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Missing value for plural 'offset:' "+prefix(start)); 12685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if((index-valueIndex)>Part.MAX_LENGTH) { 12705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 12715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Plural offset value too long: "+prefix(valueIndex)); 12725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parseDouble(valueIndex, index, false); // adds ARG_INT or ARG_DOUBLE 12745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath isEmpty=false; 12755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath continue; // no message fragment after the offset 12765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 12775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // normal selector word 12785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(length>Part.MAX_LENGTH) { 12795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException( 12805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Argument selector too long: "+prefix(selectorIndex)); 12815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_SELECTOR, selectorIndex, length, 0); 12835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(msg.regionMatches(selectorIndex, "other", 0, length)) { 12845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath hasOther=true; 12855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 12895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // parse the message fragment following the selector 12905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=skipWhiteSpace(index); 12915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==msg.length() || msg.charAt(index)!='{') { 12925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IllegalArgumentException( 12935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "No message fragment after "+ 12945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath argType.toString().toLowerCase(Locale.ENGLISH)+ 12955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath " selector: "+prefix(selectorIndex)); 12965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 12975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath index=parseMessage(index, 1, nestingLevel+1, argType); 12985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath isEmpty=false; 12995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Validates and parses an argument name or argument number string. 13045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * This internal method assumes that the input substring is a "pattern identifier". 13055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return >=0 if the name is a valid number, 13065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, 13075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * ARG_NAME_NOT_VALID (-2) if it is neither. 13085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @see #validateArgumentName(String) 13095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 13105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static int parseArgNumber(CharSequence s, int start, int limit) { 13115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // If the identifier contains only ASCII digits, then it is an argument _number_ 13125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // and must not have leading zeros (except "0" itself). 13135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Otherwise it is an argument _name_. 13145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(start>=limit) { 13155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARG_NAME_NOT_VALID; 13165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int number; 13185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Defer numeric errors until we know there are only digits. 13195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath boolean badNumber; 13205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=s.charAt(start++); 13215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='0') { 13225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(start==limit) { 13235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 0; 13245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath number=0; 13265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath badNumber=true; // leading zero 13275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if('1'<=c && c<='9') { 13295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath number=c-'0'; 13305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath badNumber=false; 13315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARG_NAME_NOT_NUMBER; 13335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while(start<limit) { 13355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=s.charAt(start++); 13365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if('0'<=c && c<='9') { 13375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(number>=Integer.MAX_VALUE/10) { 13385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath badNumber=true; // overflow 13395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath number=number*10+(c-'0'); 13415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARG_NAME_NOT_NUMBER; 13435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // There are only ASCII digits. 13465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(badNumber) { 13475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ARG_NAME_NOT_VALID; 13485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return number; 13505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int parseArgNumber(int start, int limit) { 13545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return parseArgNumber(msg, start, limit); 13555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 13575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 13585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Parses a number from the specified message substring. 13595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param start start index into the message string 13605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param limit limit index into the message string, must be start<limit 13615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param allowInfinity true if U+221E is allowed (for ChoiceFormat) 13625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 13635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void parseDouble(int start, int limit, boolean allowInfinity) { 13645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath assert start<limit; 13655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // fake loop for easy exit and single throw statement 13665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 13675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // fast path for small integers and infinity 13685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int value=0; 13695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int isNegative=0; // not boolean so that we can easily add it to value 13705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int index=start; 13715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index++); 13725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c=='-') { 13735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath isNegative=1; 13745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==limit) { 13755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; // no number 13765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=msg.charAt(index++); 13785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else if(c=='+') { 13795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==limit) { 13805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; // no number 13815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=msg.charAt(index++); 13835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(c==0x221e) { // infinity 13855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(allowInfinity && index==limit) { 13865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addArgDoublePart( 13875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath isNegative!=0 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY, 13885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath start, limit-start); 13895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 13905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 13915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 13925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 13945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // try to parse the number as a small integer but fall back to a double 13955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while('0'<=c && c<='9') { 13965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath value=value*10+(c-'0'); 13975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(value>(Part.MAX_VALUE+isNegative)) { 13985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; // not a small-enough integer 13995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(index==limit) { 14015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_INT, start, limit-start, isNegative!=0 ? -value : value); 14025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 14035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath c=msg.charAt(index++); 14055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Let Double.parseDouble() throw a NumberFormatException. 14075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath double numericValue=Double.parseDouble(msg.substring(start, limit)); 14085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addArgDoublePart(numericValue, start, limit-start); 14095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return; 14105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new NumberFormatException( 14125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath "Bad syntax for numeric value: "+msg.substring(start, limit)); 14135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Appends the s[start, limit[ substring to sb, but with only half of the apostrophes 14175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * according to JDK pattern behavior. 14185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @internal 14195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /* package */ static void appendReducedApostrophes(String s, int start, int limit, 14215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder sb) { 14225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int doubleApos=-1; 14235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath for(;;) { 14245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int i=s.indexOf('\'', start); 14255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(i<0 || i>=limit) { 14265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(s, start, limit); 14275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 14285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(i==doubleApos) { 14305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Double apostrophe at start-1 and start==i, append one. 14315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append('\''); 14325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++start; 14335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath doubleApos=-1; 14345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 14355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // Append text between apostrophes and skip this one. 14365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath sb.append(s, start, i); 14375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath doubleApos=start=i+1; 14385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int skipWhiteSpace(int index) { 14435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return PatternProps.skipWhiteSpace(msg, index); 14445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int skipIdentifier(int index) { 14475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return PatternProps.skipIdentifier(msg, index); 14485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 14515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Skips a sequence of characters that could occur in a double value. 14525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Does not fully parse or validate the value. 14535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 14545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private int skipDouble(int index) { 14555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath while(index<msg.length()) { 14565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c=msg.charAt(index); 14575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // U+221E: Allow the infinity symbol, for ChoiceFormat patterns. 14585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if((c<'0' && "+-.".indexOf(c)<0) || (c>'9' && c!='e' && c!='E' && c!=0x221e)) { 14595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath break; 14605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ++index; 14625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return index; 14645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static boolean isArgTypeChar(int c) { 14675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return ('a'<=c && c<='z') || ('A'<=c && c<='Z'); 14685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean isChoice(int index) { 14715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c; 14725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 14735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='c' || c=='C') && 14745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='h' || c=='H') && 14755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='o' || c=='O') && 14765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='i' || c=='I') && 14775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='c' || c=='C') && 14785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index))=='e' || c=='E'); 14795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean isPlural(int index) { 14825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c; 14835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 14845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='p' || c=='P') && 14855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='l' || c=='L') && 14865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='u' || c=='U') && 14875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='r' || c=='R') && 14885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='a' || c=='A') && 14895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index))=='l' || c=='L'); 14905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 14915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 14925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean isSelect(int index) { 14935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c; 14945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 14955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='s' || c=='S') && 14965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='e' || c=='E') && 14975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='l' || c=='L') && 14985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='e' || c=='E') && 14995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='c' || c=='C') && 15005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index))=='t' || c=='T'); 15015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean isOrdinal(int index) { 15045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath char c; 15055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 15065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='o' || c=='O') && 15075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='r' || c=='R') && 15085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='d' || c=='D') && 15095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='i' || c=='I') && 15105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='n' || c=='N') && 15115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index++))=='a' || c=='A') && 15125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ((c=msg.charAt(index))=='l' || c=='L'); 15135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15145d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15155d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 15165d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if we are inside a MessageFormat (sub-)pattern, 15175d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * as opposed to inside a top-level choice/plural/select pattern. 15185d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 15195d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean inMessageFormatPattern(int nestingLevel) { 15205d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return nestingLevel>0 || parts.get(0).type==Part.Type.MSG_START; 15215d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15225d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15235d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 15245d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return true if we are in a MessageFormat sub-pattern 15255d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * of a top-level ChoiceFormat pattern. 15265d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 15275d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean inTopLevelChoiceMessage(int nestingLevel, ArgType parentType) { 15285d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return 15295d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath nestingLevel==1 && 15305d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parentType==ArgType.CHOICE && 15315d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.get(0).type!=Part.Type.MSG_START; 15325d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15335d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15345d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void addPart(Part.Type type, int index, int length, int value) { 15355d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.add(new Part(type, index, length, value)); 15365d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15375d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15385d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void addLimitPart(int start, Part.Type type, int index, int length, int value) { 15395d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath parts.get(start).limitPartIndex=parts.size(); 15405d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(type, index, length, value); 15415d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15425d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15435d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private void addArgDoublePart(double numericValue, int start, int length) { 15445d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int numericIndex; 15455d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(numericValues==null) { 15465d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericValues=new ArrayList<Double>(); 15475d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericIndex=0; 15485d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15495d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericIndex=numericValues.size(); 15505d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(numericIndex>Part.MAX_VALUE) { 15515d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath throw new IndexOutOfBoundsException("Too many numeric values"); 15525d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15535d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15545d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath numericValues.add(numericValue); 15555d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath addPart(Part.Type.ARG_DOUBLE, start, length, numericIndex); 15565d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15575d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15585d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final int MAX_PREFIX_LENGTH=24; 15595d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15605d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath /** 15615d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * Returns a prefix of s.substring(start). Used for Exception messages. 15625d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param s 15635d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @param start start index in s 15645d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath * @return s.substring(start) or a prefix of that 15655d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath */ 15665d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static String prefix(String s, int start) { 15675d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath StringBuilder prefix=new StringBuilder(MAX_PREFIX_LENGTH+20); 15685d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(start==0) { 15695d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix.append("\""); 15705d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15715d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix.append("[at pattern index ").append(start).append("] \""); 15725d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15735d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int substringLength=s.length()-start; 15745d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(substringLength<=MAX_PREFIX_LENGTH) { 15755d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix.append(start==0 ? s : s.substring(start)); 15765d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } else { 15775d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath int limit=start+MAX_PREFIX_LENGTH-4; 15785d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath if(Character.isHighSurrogate(s.charAt(limit-1))) { 15795d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath // remove lead surrogate from the end of the prefix 15805d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath --limit; 15815d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15825d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath prefix.append(s, start, limit).append(" ..."); 15835d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15845d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return prefix.append("\"").toString(); 15855d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15865d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15875d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static String prefix(String s) { 15885d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return prefix(s, 0); 15895d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15905d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15915d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private String prefix(int start) { 15925d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return prefix(msg, start); 15935d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15945d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15955d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private String prefix() { 15965d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath return prefix(msg, 0); 15975d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath } 15985d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 15995d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private ApostropheMode aposMode; 16005d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private String msg; 16015d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private ArrayList<Part> parts=new ArrayList<Part>(); 16025d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private ArrayList<Double> numericValues; 16035d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean hasArgNames; 16045d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean hasArgNumbers; 16055d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean needsAutoQuoting; 16065d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private boolean frozen; 16075d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 16085d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final ApostropheMode defaultAposMode= 16095d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ApostropheMode.valueOf( 16105d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath ICUConfig.get("com.ibm.icu.text.MessagePattern.ApostropheMode", "DOUBLE_OPTIONAL")); 16115d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath 16125d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath private static final ArgType[] argTypes=ArgType.values(); 16135d1b0c9f348c25f01c6f2f5be04d2409409c08feNarayan Kamath} 1614