From aadf36809c5daee8897601d33d8e6d6cedb57b9b Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Tue, 12 Dec 2023 19:25:20 +0000 Subject: [PATCH] 6230751: [Fmt-Ch] Recursive MessageFormats in ChoiceFormats ignore indicated subformats Reviewed-by: naoto --- .../share/classes/java/text/ChoiceFormat.java | 44 +++++++---------- .../classes/java/text/MessageFormat.java | 49 +++++++------------ 2 files changed, 36 insertions(+), 57 deletions(-) diff --git a/src/java.base/share/classes/java/text/ChoiceFormat.java b/src/java.base/share/classes/java/text/ChoiceFormat.java index 7845ae9cab3..883c8b5d46b 100644 --- a/src/java.base/share/classes/java/text/ChoiceFormat.java +++ b/src/java.base/share/classes/java/text/ChoiceFormat.java @@ -88,7 +88,6 @@ import java.util.Arrays; *

* 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"}; @@ -100,34 +99,27 @@ import java.util.Arrays; * + form.parse(form.format(i),status)); * } * } - *
+ * + *

* For more sophisticated patterns, {@code ChoiceFormat} can be used with * {@link MessageFormat} to produce accurate forms for singular and plural: - *

* {@snippet lang=java : - * double[] filelimits = {0,1,2}; - * String[] filepart = {"are no files","is one file","are {2} files"}; - * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart); - * Format[] testFormats = {fileform, null, NumberFormat.getInstance()}; - * MessageFormat pattform = new MessageFormat("There {0} on {1}"); - * pattform.setFormats(testFormats); - * Object[] testArgs = {null, "ADisk", null}; - * for (int i = 0; i < 4; ++i) { - * testArgs[0] = Integer.valueOf(i); - * testArgs[2] = testArgs[0]; - * System.out.println(pattform.format(testArgs)); + * 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)); * } - * } - *
- * Would output the following: - *
- *
{@code
- * There are no files on ADisk
- * There is one file on ADisk
- * There are 2 files on ADisk
- * There are 3 files on ADisk
- * }
- *
+ * The output with different values for {@code fileCount}: + *
+ * The disk "MyDisk" contains no files.
+ * The disk "MyDisk" contains one file.
+ * The disk "MyDisk" contains 1,273 files.
+ * 
+ * See {@link MessageFormat##pattern_caveats MessageFormat} for caveats regarding + * {@code MessageFormat} patterns within a {@code ChoiceFormat} pattern. * *

Patterns

* A {@code ChoiceFormat} pattern has the following syntax: @@ -194,7 +186,6 @@ import java.util.Arrays; * {@code new ChoiceFormat("1# ''one'' ").format(1)} returns {@code " 'one' "}. * *

Below is an example of constructing a ChoiceFormat with a pattern: - *

* {@snippet lang=java : * ChoiceFormat fmt = new ChoiceFormat( * "-1#is negative| 0#is zero or fraction | 1#is one |1.0 * *

Synchronization

* diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 30ce4f005a7..1f265c63da8 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -231,7 +231,6 @@ import java.util.Objects; *

* The first example uses the static method {@code MessageFormat.format}, * which internally creates a {@code MessageFormat} for one-time use: - *

* {@snippet lang=java : * int planet = 7; * String event = "a disturbance in the Force"; @@ -240,7 +239,6 @@ import java.util.Objects; * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", * planet, new Date(), event); * } - *
* The output is: *
  * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
@@ -249,7 +247,6 @@ import java.util.Objects;
  * 

* The following example creates a {@code MessageFormat} instance that * can be used repeatedly: - *

* {@snippet lang=java : * int fileCount = 1273; * String diskName = "MyDisk"; @@ -260,7 +257,6 @@ import java.util.Objects; * * System.out.println(form.format(testArgs)); * } - *
* The output with different values for {@code fileCount}: *
  * The disk "MyDisk" contains 0 file(s).
@@ -269,23 +265,17 @@ import java.util.Objects;
  * 
* *

- * For more sophisticated patterns, you can use a {@code ChoiceFormat} - * to produce correct forms for singular and plural: - *

+ * For more sophisticated patterns, {@link ChoiceFormat} can be used with + * {@code MessageFormat} to produce accurate forms for singular and plural: * {@snippet lang=java : - * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}."); - * double[] filelimits = {0,1,2}; - * String[] filepart = {"no files","one file","{0,number} files"}; - * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart); - * form.setFormatByArgumentIndex(0, fileform); - * - * int fileCount = 1273; - * String diskName = "MyDisk"; - * Object[] testArgs = {Long.valueOf(fileCount), diskName}; - * - * System.out.println(form.format(testArgs)); + * 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}: *
  * The disk "MyDisk" contains no files.
@@ -297,24 +287,26 @@ import java.util.Objects;
  * You can create the {@code ChoiceFormat} programmatically, as in the
  * above example, or by using a pattern. See {@link ChoiceFormat}
  * for more information.
- * 
* {@snippet lang=java : - * form.applyPattern( - * "There {0,choice,0#are no files|1#is one file|1 * *

- * Note: As we see above, the string produced - * by a {@code ChoiceFormat} in {@code MessageFormat} is treated as special; - * occurrences of '{' are used to indicate subformats, and cause recursion. + * Notes: As seen in the previous snippet, + * the string produced by a {@code ChoiceFormat} in {@code MessageFormat} is + * treated as special; occurrences of '{' are used to indicate subformats, and + * cause recursion. If a {@code FormatElement} is defined in the {@code ChoiceFormat} + * pattern, it will only be formatted according to the {@code FormatType} and + * {@code FormatStyle} pattern provided. The associated subformats of the + * top level {@code MessageFormat} will not be applied to the {@code FormatElement} + * defined in the {@code ChoiceFormat} pattern. * If you create both a {@code MessageFormat} and {@code ChoiceFormat} * programmatically (instead of using the string patterns), then be careful not to * produce a format that recurses on itself, which will cause an infinite loop. *

* When a single argument is parsed more than once in the string, the last match * will be the final result of the parsing. For example, - *

* {@snippet lang=java : * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}"); * Object[] objs = {Double.valueOf(3.1415)}; @@ -323,20 +315,17 @@ import java.util.Objects; * objs = mf.parse(result, new ParsePosition(0)); * // objs now equals {Double.valueOf(3.1)} * } - *
* *

* Likewise, parsing with a {@code MessageFormat} object using patterns containing * multiple occurrences of the same argument would return the last match. For * example, - *

* {@snippet lang=java : * MessageFormat mf = new MessageFormat("{0}, {0}, {0}"); * String forParsing = "x, y, z"; * Object[] objs = mf.parse(forParsing, new ParsePosition(0)); * // objs now equals {new String("z")} * } - *
* *

Synchronization

*