6285888: ChoiceFormat can support unescaped relational symbols in the Format segment

Reviewed-by: naoto
This commit is contained in:
Justin Lu 2024-02-01 22:21:45 +00:00
parent 144a08ee50
commit d3c3194ac3
3 changed files with 153 additions and 65 deletions

View file

@ -68,71 +68,18 @@ import java.util.Arrays;
* doesn't require any complex setup for a given locale. In fact,
* {@code ChoiceFormat} doesn't implement any locale specific behavior.
*
* <p>
* A {@code ChoiceFormat} can be constructed using either an array of formats
* and an array of limits or a string pattern. When constructing with
* format and limit arrays, the length of these arrays must be the same.
*
* For example,
* <ul>
* <li>
* <em>limits</em> = {1,2,3,4,5,6,7}<br>
* <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
* <li>
* <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
* <em>formats</em> = {"no files", "one file", "many files"}<br>
* ({@code nextDouble} can be used to get the next higher double, to
* make the half-open interval.)
* </ul>
*
* <p>
* Below is an example of constructing a ChoiceFormat with arrays to format
* and parse values:
* {@snippet lang=java :
* double[] limits = {1,2,3,4,5,6,7};
* String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
* ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
* ParsePosition status = new ParsePosition(0);
* for (double i = 0.0; i <= 8.0; ++i) {
* status.setIndex(0);
* System.out.println(i + " -> " + form.format(i) + " -> "
* + form.parse(form.format(i),status));
* }
* }
*
* <p>
* For more sophisticated patterns, {@code ChoiceFormat} can be used with
* {@link MessageFormat} to produce accurate forms for singular and plural:
* {@snippet lang=java :
* MessageFormat msgFmt = new MessageFormat("The disk \"{0}\" contains {1}.");
* double[] fileLimits = {0,1,2};
* String[] filePart = {"no files","one file","{1,number} files"};
* ChoiceFormat fileChoices = new ChoiceFormat(fileLimits, filePart);
* msgFmt.setFormatByArgumentIndex(1, fileChoices);
* Object[] args = {"MyDisk", 1273};
* System.out.println(msgFmt.format(args));
* }
* The output with different values for {@code fileCount}:
* <blockquote><pre>
* The disk "MyDisk" contains no files.
* The disk "MyDisk" contains one file.
* The disk "MyDisk" contains 1,273 files.
* </pre></blockquote>
* See {@link MessageFormat##pattern_caveats MessageFormat} for caveats regarding
* {@code MessageFormat} patterns within a {@code ChoiceFormat} pattern.
*
* <h2><a id="patterns">Patterns</a></h2>
* A {@code ChoiceFormat} pattern has the following syntax:
* <blockquote>
* <dl>
* <dt><i>Pattern:</i>
* <dd>SubPattern *("|" SubPattern)
* <dd><i>Note: Each additional SubPattern must have a Limit greater than the previous SubPattern's Limit</i>
* </dl>
*
* <dl>
* <dt><i>SubPattern:</i>
* <dd>Limit Relation Format
* <dd><sub>Note: Each additional SubPattern must have an ascending Limit-Relation interval</sub></dd>
* </dl>
*
* <dl>
@ -172,20 +119,54 @@ import java.util.Arrays;
*
* <dl>
* <dt><i>Format:</i>
* <dd>Any characters except the <i>Relation</i> symbols
* <dd>Any characters except the special pattern character '|'
* </dl>
*
* </blockquote>
*
* <i>Note:The relation &le; is not equivalent to &lt;&equals;</i>
*
* <p>If a <i>Relation</i> symbol is to be used within a <i>Format</i> pattern,
* it must be single quoted. For example,
* {@code new ChoiceFormat("1# '#'1 ").format(1)} returns {@code " #1 "}.
* <p> To use a reserved special pattern character within a <i>Format</i> pattern,
* it must be single quoted. For example, {@code new ChoiceFormat("1#'|'foo'|'").format(1)}
* returns {@code "|foo|"}.
* Use two single quotes in a row to produce a literal single quote. For example,
* {@code new ChoiceFormat("1# ''one'' ").format(1)} returns {@code " 'one' "}.
*
* <p>Below is an example of constructing a ChoiceFormat with a pattern:
* <h2>Usage Information</h2>
*
* <p>
* A {@code ChoiceFormat} can be constructed using either an array of formats
* and an array of limits or a string pattern. When constructing with
* format and limit arrays, the length of these arrays must be the same.
*
* For example,
* <ul>
* <li>
* <em>limits</em> = {1,2,3,4,5,6,7}<br>
* <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
* <li>
* <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
* <em>formats</em> = {"no files", "one file", "many files"}<br>
* ({@code nextDouble} can be used to get the next higher double, to
* make the half-open interval.)
* </ul>
*
* <p>
* Below is an example of constructing a ChoiceFormat with arrays to format
* and parse values:
* {@snippet lang=java :
* double[] limits = {1,2,3,4,5,6,7};
* String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
* ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
* ParsePosition status = new ParsePosition(0);
* for (double i = 0.0; i <= 8.0; ++i) {
* status.setIndex(0);
* System.out.println(i + " -> " + form.format(i) + " -> "
* + form.parse(form.format(i),status));
* }
* }
*
* <p>Below is an example of constructing a ChoiceFormat with a String pattern:
* {@snippet lang=java :
* ChoiceFormat fmt = new ChoiceFormat(
* "-1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+ |2#is two |2<is more than 2.");
@ -202,6 +183,27 @@ import java.util.Arrays;
* System.out.println(fmt.format(Double.POSITIVE_INFINITY)); // outputs "is more than 2."
* }
*
* <p>
* For more sophisticated patterns, {@code ChoiceFormat} can be used with
* {@link MessageFormat} to produce accurate forms for singular and plural:
* {@snippet lang=java :
* MessageFormat msgFmt = new MessageFormat("The disk \"{0}\" contains {1}.");
* double[] fileLimits = {0,1,2};
* String[] filePart = {"no files","one file","{1,number} files"};
* ChoiceFormat fileChoices = new ChoiceFormat(fileLimits, filePart);
* msgFmt.setFormatByArgumentIndex(1, fileChoices);
* Object[] args = {"MyDisk", 1273};
* System.out.println(msgFmt.format(args));
* }
* The output with different values for {@code fileCount}:
* <blockquote><pre>
* The disk "MyDisk" contains no files.
* The disk "MyDisk" contains one file.
* The disk "MyDisk" contains 1,273 files.
* </pre></blockquote>
* See {@link MessageFormat##pattern_caveats MessageFormat} for caveats regarding
* {@code MessageFormat} patterns within a {@code ChoiceFormat} pattern.
*
* <h2><a id="synchronization">Synchronization</a></h2>
*
* <p>
@ -254,7 +256,7 @@ public class ChoiceFormat extends NumberFormat {
double[] newChoiceLimits = new double[30];
String[] newChoiceFormats = new String[30];
int count = 0;
int part = 0;
int part = 0; // 0 denotes limit, 1 denotes format
double startValue = 0;
double oldStartValue = Double.NaN;
boolean inQuote = false;
@ -270,7 +272,10 @@ public class ChoiceFormat extends NumberFormat {
}
} else if (inQuote) {
segments[part].append(ch);
} else if (ch == '<' || ch == '#' || ch == '\u2264') {
} else if (part == 0 && (ch == '<' || ch == '#' || ch == '\u2264')) {
// Only consider relational symbols if parsing the limit segment (part == 0).
// Don't treat a relational symbol as syntactically significant
// when parsing Format segment (part == 1)
if (segments[0].length() == 0) {
throw new IllegalArgumentException("Each interval must"
+ " contain a number before a format");