Class MessagePattern
- All Implemented Interfaces:
Freezable<MessagePattern>
,Cloneable
The parser handles all syntax relevant for identifying message arguments. This includes "complex" arguments whose style strings contain nested MessageFormat pattern substrings. For "simple" arguments (with no nested MessageFormat pattern substrings), the argument style is not parsed any further.
The parser handles named and numbered message arguments and allows both in one message.
Once a pattern has been parsed successfully, iterate through the parsed data with countParts(), getPart() and related methods.
The data logically represents a parse tree, but is stored and accessed
as a list of "parts" for fast and simple parsing and to minimize object allocations.
Arguments and nested messages are best handled via recursion.
For every _START "part", getLimitPartIndex(int)
efficiently returns
the index of the corresponding _LIMIT "part".
List of "parts":
message = MSG_START (SKIP_SYNTAX | INSERT_CHAR | REPLACE_NUMBER | argument)* MSG_LIMIT argument = noneArg | simpleArg | complexArg complexArg = choiceArg | pluralArg | selectArg noneArg = ARG_START.NONE (ARG_NAME | ARG_NUMBER) ARG_LIMIT.NONE simpleArg = ARG_START.SIMPLE (ARG_NAME | ARG_NUMBER) ARG_TYPE [ARG_STYLE] ARG_LIMIT.SIMPLE choiceArg = ARG_START.CHOICE (ARG_NAME | ARG_NUMBER) choiceStyle ARG_LIMIT.CHOICE pluralArg = ARG_START.PLURAL (ARG_NAME | ARG_NUMBER) pluralStyle ARG_LIMIT.PLURAL selectArg = ARG_START.SELECT (ARG_NAME | ARG_NUMBER) selectStyle ARG_LIMIT.SELECT choiceStyle = ((ARG_INT | ARG_DOUBLE) ARG_SELECTOR message)+ pluralStyle = [ARG_INT | ARG_DOUBLE] (ARG_SELECTOR [ARG_INT | ARG_DOUBLE] message)+ selectStyle = (ARG_SELECTOR message)+
- Literal output text is not represented directly by "parts" but accessed between parts of a message, from one part's getLimit() to the next part's getIndex().
ARG_START.CHOICE
stands for an ARG_START Part with ArgType CHOICE.- In the choiceStyle, the ARG_SELECTOR has the '<', the '#' or the less-than-or-equal-to sign (U+2264).
- In the pluralStyle, the first, optional numeric Part has the "offset:" value. The optional numeric Part between each (ARG_SELECTOR, message) pair is the value of an explicit-number selector like "=2", otherwise the selector is a non-numeric identifier.
- The REPLACE_NUMBER Part can occur only in an immediate sub-message of the pluralStyle.
This class is not intended for public subclassing.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enum
Mode for when an apostrophe starts quoted literal text for MessageFormat output.static enum
Argument type constants.static final class
A message pattern "part", representing a pattern parsing event. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate MessagePattern.ApostropheMode
static final int
Return value fromvalidateArgumentName(String)
for when the string is a valid "pattern identifier" but not a number.static final int
Return value fromvalidateArgumentName(String)
for when the string is invalid.private static final MessagePattern.ArgType[]
private static final MessagePattern.ApostropheMode
private boolean
private boolean
private boolean
private static final int
private String
private boolean
static final double
Special value that is returned by getNumericValue(Part) when no numeric value is defined for a part.private ArrayList<MessagePattern.Part>
-
Constructor Summary
ConstructorsConstructorDescriptionConstructs an empty MessagePattern with default ApostropheMode.Constructs an empty MessagePattern.MessagePattern
(String pattern) Constructs a MessagePattern with default ApostropheMode and parses the MessageFormat pattern string. -
Method Summary
Modifier and TypeMethodDescriptionprivate void
addArgDoublePart
(double numericValue, int start, int length) private void
addLimitPart
(int start, MessagePattern.Part.Type type, int index, int length, int value) private void
addPart
(MessagePattern.Part.Type type, int index, int length, int value) (package private) static void
appendReducedApostrophes
(String s, int start, int limit, StringBuilder sb) Appends the s[start, limit[ substring to sb, but with only half of the apostrophes according to JDK pattern behavior.Returns a version of the parsed pattern string where each ASCII apostrophe is doubled (escaped) if it is not already, and if it is not interpreted as quoting syntax.void
clear()
Clears this MessagePattern.void
Clears this MessagePattern and sets the ApostropheMode.clone()
Creates and returns a copy of this object.Creates and returns an unfrozen copy of this object.int
Returns the number of "parts" created by parsing the pattern string.boolean
freeze()
Freezes this object, making it immutable and thread-safe.int
getLimitPartIndex
(int start) Returns the index of the ARG|MSG_LIMIT part corresponding to the ARG|MSG_START at start.double
Returns the numeric value associated with an ARG_INT or ARG_DOUBLE.getPart
(int i) Gets the i-th pattern "part".getPartType
(int i) Returns the Part.Type of the i-th pattern "part".int
getPatternIndex
(int partIndex) Returns the pattern index of the specified pattern "part".double
getPluralOffset
(int pluralStart) Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified.Returns the substring of the pattern string indicated by the Part.int
hashCode()
boolean
Does the parsed pattern have named arguments like {first_name}?boolean
Does the parsed pattern have numbered arguments like {2}?private boolean
inMessageFormatPattern
(int nestingLevel) private boolean
inTopLevelChoiceMessage
(int nestingLevel, MessagePattern.ArgType parentType) private static boolean
isArgTypeChar
(int c) private boolean
isChoice
(int index) boolean
isFrozen()
Determines whether this object is frozen (immutable) or not.private boolean
isOrdinal
(int index) private boolean
isPlural
(int index) private boolean
isSelect
(int index) (package private) boolean
Parses a MessageFormat pattern string.private int
parseArg
(int index, int argStartLength, int nestingLevel) private int
parseArgNumber
(int start, int limit) private static int
parseArgNumber
(CharSequence s, int start, int limit) Validates and parses an argument name or argument number string.private int
parseChoiceStyle
(int index, int nestingLevel) parseChoiceStyle
(String pattern) Parses a ChoiceFormat pattern string.private void
parseDouble
(int start, int limit, boolean allowInfinity) Parses a number from the specified message substring.private int
parseMessage
(int index, int msgStartLength, int nestingLevel, MessagePattern.ArgType parentType) private int
parsePluralOrSelectStyle
(MessagePattern.ArgType argType, int index, int nestingLevel) parsePluralStyle
(String pattern) Parses a PluralFormat pattern string.parseSelectStyle
(String pattern) Parses a SelectFormat pattern string.private int
parseSimpleStyle
(int index) boolean
Compares the part's substring with the input string s.private void
private String
prefix()
private String
prefix
(int start) private static String
private static String
Returns a prefix of s.substring(start).private void
private int
skipDouble
(int index) Skips a sequence of characters that could occur in a double value.private int
skipIdentifier
(int index) private int
skipWhiteSpace
(int index) toString()
static int
validateArgumentName
(String name) Validates and parses an argument name or argument number string.
-
Field Details
-
ARG_NAME_NOT_NUMBER
public static final int ARG_NAME_NOT_NUMBERReturn value fromvalidateArgumentName(String)
for when the string is a valid "pattern identifier" but not a number.- See Also:
-
ARG_NAME_NOT_VALID
public static final int ARG_NAME_NOT_VALIDReturn value fromvalidateArgumentName(String)
for when the string is invalid. It might not be a valid "pattern identifier", or it have only ASCII digits but there is a leading zero or the number is too large.- See Also:
-
NO_NUMERIC_VALUE
public static final double NO_NUMERIC_VALUESpecial value that is returned by getNumericValue(Part) when no numeric value is defined for a part. -
MAX_PREFIX_LENGTH
private static final int MAX_PREFIX_LENGTH- See Also:
-
aposMode
-
msg
-
parts
-
numericValues
-
hasArgNames
private boolean hasArgNames -
hasArgNumbers
private boolean hasArgNumbers -
needsAutoQuoting
private boolean needsAutoQuoting -
frozen
private volatile boolean frozen -
defaultAposMode
-
argTypes
-
-
Constructor Details
-
MessagePattern
public MessagePattern()Constructs an empty MessagePattern with default ApostropheMode. -
MessagePattern
Constructs an empty MessagePattern.- Parameters:
mode
- Explicit ApostropheMode.
-
MessagePattern
Constructs a MessagePattern with default ApostropheMode and parses the MessageFormat pattern string.- Parameters:
pattern
- a MessageFormat pattern string- Throws:
IllegalArgumentException
- for syntax errors in the pattern stringIndexOutOfBoundsException
- if certain limits are exceeded (e.g., argument number too high, argument name too long, etc.)NumberFormatException
- if a number could not be parsed
-
-
Method Details
-
parse
Parses a MessageFormat pattern string.- Parameters:
pattern
- a MessageFormat pattern string- Returns:
- this
- Throws:
IllegalArgumentException
- for syntax errors in the pattern stringIndexOutOfBoundsException
- if certain limits are exceeded (e.g., argument number too high, argument name too long, etc.)NumberFormatException
- if a number could not be parsed
-
parseChoiceStyle
Parses a ChoiceFormat pattern string.- Parameters:
pattern
- a ChoiceFormat pattern string- Returns:
- this
- Throws:
IllegalArgumentException
- for syntax errors in the pattern stringIndexOutOfBoundsException
- if certain limits are exceeded (e.g., argument number too high, argument name too long, etc.)NumberFormatException
- if a number could not be parsed
-
parsePluralStyle
Parses a PluralFormat pattern string.- Parameters:
pattern
- a PluralFormat pattern string- Returns:
- this
- Throws:
IllegalArgumentException
- for syntax errors in the pattern stringIndexOutOfBoundsException
- if certain limits are exceeded (e.g., argument number too high, argument name too long, etc.)NumberFormatException
- if a number could not be parsed
-
parseSelectStyle
Parses a SelectFormat pattern string.- Parameters:
pattern
- a SelectFormat pattern string- Returns:
- this
- Throws:
IllegalArgumentException
- for syntax errors in the pattern stringIndexOutOfBoundsException
- if certain limits are exceeded (e.g., argument number too high, argument name too long, etc.)NumberFormatException
- if a number could not be parsed
-
clear
public void clear()Clears this MessagePattern. countParts() will return 0. -
clearPatternAndSetApostropheMode
Clears this MessagePattern and sets the ApostropheMode. countParts() will return 0.- Parameters:
mode
- The new ApostropheMode.
-
equals
-
hashCode
public int hashCode() -
getApostropheMode
- Returns:
- this instance's ApostropheMode.
-
jdkAposMode
boolean jdkAposMode()- Returns:
- true if getApostropheMode() == ApostropheMode.DOUBLE_REQUIRED
-
getPatternString
- Returns:
- the parsed pattern string (null if none was parsed).
-
hasNamedArguments
public boolean hasNamedArguments()Does the parsed pattern have named arguments like {first_name}?- Returns:
- true if the parsed pattern has at least one named argument.
-
hasNumberedArguments
public boolean hasNumberedArguments()Does the parsed pattern have numbered arguments like {2}?- Returns:
- true if the parsed pattern has at least one numbered argument.
-
toString
-
validateArgumentName
Validates and parses an argument name or argument number string. An argument name must be a "pattern identifier", that is, it must contain no Unicode Pattern_Syntax or Pattern_White_Space characters. If it only contains ASCII digits, then it must be a small integer with no leading zero.- Parameters:
name
- Input string.- Returns:
- >=0 if the name is a valid number, ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, ARG_NAME_NOT_VALID (-2) if it is neither.
-
autoQuoteApostropheDeep
Returns a version of the parsed pattern string where each ASCII apostrophe is doubled (escaped) if it is not already, and if it is not interpreted as quoting syntax.For example, this turns "I don't '{know}' {gender,select,female{h''er}other{h'im}}." into "I don''t '{know}' {gender,select,female{h''er}other{h''im}}."
- Returns:
- the deep-auto-quoted version of the parsed pattern string.
- See Also:
-
countParts
public int countParts()Returns the number of "parts" created by parsing the pattern string. Returns 0 if no pattern has been parsed or clear() was called.- Returns:
- the number of pattern parts.
-
getPart
Gets the i-th pattern "part".- Parameters:
i
- The index of the Part data. (0..countParts()-1)- Returns:
- the i-th pattern "part".
- Throws:
IndexOutOfBoundsException
- if i is outside the (0..countParts()-1) range
-
getPartType
Returns the Part.Type of the i-th pattern "part". Convenience method for getPart(i).getType().- Parameters:
i
- The index of the Part data. (0..countParts()-1)- Returns:
- The Part.Type of the i-th Part.
- Throws:
IndexOutOfBoundsException
- if i is outside the (0..countParts()-1) range
-
getPatternIndex
public int getPatternIndex(int partIndex) Returns the pattern index of the specified pattern "part". Convenience method for getPart(partIndex).getIndex().- Parameters:
partIndex
- The index of the Part data. (0..countParts()-1)- Returns:
- The pattern index of this Part.
- Throws:
IndexOutOfBoundsException
- if partIndex is outside the (0..countParts()-1) range
-
getSubstring
Returns the substring of the pattern string indicated by the Part. Convenience method for getPatternString().substring(part.getIndex(), part.getLimit()).- Parameters:
part
- a part of this MessagePattern.- Returns:
- the substring associated with part.
-
partSubstringMatches
Compares the part's substring with the input string s.- Parameters:
part
- a part of this MessagePattern.s
- a string.- Returns:
- true if getSubstring(part).equals(s).
-
getNumericValue
Returns the numeric value associated with an ARG_INT or ARG_DOUBLE.- Parameters:
part
- a part of this MessagePattern.- Returns:
- the part's numeric value, or NO_NUMERIC_VALUE if this is not a numeric part.
-
getPluralOffset
public double getPluralOffset(int pluralStart) Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified.- Parameters:
pluralStart
- the index of the first PluralFormat argument style part. (0..countParts()-1)- Returns:
- the "offset:" value.
- Throws:
IndexOutOfBoundsException
- if pluralStart is outside the (0..countParts()-1) range
-
getLimitPartIndex
public int getLimitPartIndex(int start) Returns the index of the ARG|MSG_LIMIT part corresponding to the ARG|MSG_START at start.- Parameters:
start
- The index of some Part data (0..countParts()-1); this Part should be of Type ARG_START or MSG_START.- Returns:
- The first i>start where getPart(i).getType()==ARG|MSG_LIMIT at the same nesting level, or start itself if getPartType(msgStart)!=ARG|MSG_START.
- Throws:
IndexOutOfBoundsException
- if start is outside the (0..countParts()-1) range
-
clone
Creates and returns a copy of this object. -
cloneAsThawed
Creates and returns an unfrozen copy of this object.- Specified by:
cloneAsThawed
in interfaceFreezable<MessagePattern>
- Returns:
- a copy of this object.
-
freeze
Freezes this object, making it immutable and thread-safe.- Specified by:
freeze
in interfaceFreezable<MessagePattern>
- Returns:
- this
-
isFrozen
public boolean isFrozen()Determines whether this object is frozen (immutable) or not.- Specified by:
isFrozen
in interfaceFreezable<MessagePattern>
- Returns:
- true if this object is frozen.
-
preParse
-
postParse
private void postParse() -
parseMessage
private int parseMessage(int index, int msgStartLength, int nestingLevel, MessagePattern.ArgType parentType) -
parseArg
private int parseArg(int index, int argStartLength, int nestingLevel) -
parseSimpleStyle
private int parseSimpleStyle(int index) -
parseChoiceStyle
private int parseChoiceStyle(int index, int nestingLevel) -
parsePluralOrSelectStyle
-
parseArgNumber
Validates and parses an argument name or argument number string. This internal method assumes that the input substring is a "pattern identifier".- Returns:
- >=0 if the name is a valid number, ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, ARG_NAME_NOT_VALID (-2) if it is neither.
- See Also:
-
parseArgNumber
private int parseArgNumber(int start, int limit) -
parseDouble
private void parseDouble(int start, int limit, boolean allowInfinity) Parses a number from the specified message substring.- Parameters:
start
- start index into the message stringlimit
- limit index into the message string, must be start<limitallowInfinity
- true if U+221E is allowed (for ChoiceFormat)
-
appendReducedApostrophes
Appends the s[start, limit[ substring to sb, but with only half of the apostrophes according to JDK pattern behavior. -
skipWhiteSpace
private int skipWhiteSpace(int index) -
skipIdentifier
private int skipIdentifier(int index) -
skipDouble
private int skipDouble(int index) Skips a sequence of characters that could occur in a double value. Does not fully parse or validate the value. -
isArgTypeChar
private static boolean isArgTypeChar(int c) -
isChoice
private boolean isChoice(int index) -
isPlural
private boolean isPlural(int index) -
isSelect
private boolean isSelect(int index) -
isOrdinal
private boolean isOrdinal(int index) -
inMessageFormatPattern
private boolean inMessageFormatPattern(int nestingLevel) - Returns:
- true if we are inside a MessageFormat (sub-)pattern, as opposed to inside a top-level choice/plural/select pattern.
-
inTopLevelChoiceMessage
- Returns:
- true if we are in a MessageFormat sub-pattern of a top-level ChoiceFormat pattern.
-
addPart
-
addLimitPart
private void addLimitPart(int start, MessagePattern.Part.Type type, int index, int length, int value) -
addArgDoublePart
private void addArgDoublePart(double numericValue, int start, int length) -
prefix
Returns a prefix of s.substring(start). Used for Exception messages.- Parameters:
s
-start
- start index in s- Returns:
- s.substring(start) or a prefix of that
-
prefix
-
prefix
-
prefix
-