6228794: java.text.ChoiceFormat pattern behavior is not well documented.

Reviewed-by: naoto
This commit is contained in:
Justin Lu 2023-09-13 20:32:21 +00:00
parent 3b0a6d2a68
commit ce93d27fe5

View file

@ -44,8 +44,9 @@ import java.io.ObjectInputStream;
import java.util.Arrays; import java.util.Arrays;
/** /**
* A {@code ChoiceFormat} allows you to attach a format to a range of numbers. * {@code ChoiceFormat} is a concrete subclass of {@code NumberFormat} that
* It is generally used in a {@code MessageFormat} for handling plurals. * allows you to attach a format to a range of numbers.
* It is generally used in a {@link MessageFormat} for handling plurals.
* The choice is specified with an ascending list of doubles, where each item * The choice is specified with an ascending list of doubles, where each item
* specifies a half-open interval up to the next item: * specifies a half-open interval up to the next item:
* <blockquote> * <blockquote>
@ -68,8 +69,10 @@ import java.util.Arrays;
* {@code ChoiceFormat} doesn't implement any locale specific behavior. * {@code ChoiceFormat} doesn't implement any locale specific behavior.
* *
* <p> * <p>
* When creating a {@code ChoiceFormat}, you must specify an array of formats * A {@code ChoiceFormat} can be constructed using either an array of formats
* and an array of limits. The length of these arrays must be the same. * 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, * For example,
* <ul> * <ul>
* <li> * <li>
@ -83,7 +86,8 @@ import java.util.Arrays;
* </ul> * </ul>
* *
* <p> * <p>
* Here is a simple example that shows formatting and parsing: * Below is an example of constructing a ChoiceFormat with arrays to format
* and parse values:
* <blockquote> * <blockquote>
* {@snippet lang=java : * {@snippet lang=java :
* double[] limits = {1,2,3,4,5,6,7}; * double[] limits = {1,2,3,4,5,6,7};
@ -97,7 +101,8 @@ import java.util.Arrays;
* } * }
* } * }
* </blockquote> * </blockquote>
* Here is a more complex example, with a pattern format: * For more sophisticated patterns, {@code ChoiceFormat} can be used with
* {@link MessageFormat} to produce accurate forms for singular and plural:
* <blockquote> * <blockquote>
* {@snippet lang=java : * {@snippet lang=java :
* double[] filelimits = {0,1,2}; * double[] filelimits = {0,1,2};
@ -114,42 +119,92 @@ import java.util.Arrays;
* } * }
* } * }
* </blockquote> * </blockquote>
* <p> * Would output the following:
* Specifying a pattern for ChoiceFormat objects is fairly straightforward. * <blockquote>
* For example: * <pre>{@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
* }</pre>
* </blockquote>
*
* <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
* </dl>
*
* <dl>
* <dt><i>Limit:</i>
* <dd>Number / "&infin;" / "-&infin;"
* </dl>
*
* <dl>
* <dt><i>Number:</i>
* <dd>["-"] *(Digit) 1*(Decimal / Digit) *(Digit) [Exponent]
* </dl>
*
* <dl>
* <dt><i>Decimal:</i>
* <dd>1*(Digit ".") / 1*("." Digit)
* </dl>
*
* <dl>
* <dt><i>Digit:</i>
* <dd>0 - 9
* </dl>
*
* <dl>
* <dt><i>Exponent:</i>
* <dd>*(Digit) Digit ExponentSymbol Digit *(Digit)
* </dl>
*
* <dl>
* <dt><i>ExponentSymbol:</i>
* <dd>"e" / "E"
* </dl>
*
* <dl>
* <dt><i>Relation:</i>
* <dd>"#" / "&lt;" / "&le;"
* </dl>
*
* <dl>
* <dt><i>Format:</i>
* <dd>Any characters except the <i>Relation</i> symbols
* </dl>
*
* </blockquote>
*
* <i>Note:The relation &le; is not equivalent to &lt;&equals;</i>
*
* <p>Below is an example of constructing a ChoiceFormat with a pattern:
* <blockquote> * <blockquote>
* {@snippet lang=java : * {@snippet lang=java :
* ChoiceFormat fmt = new ChoiceFormat( * 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."); * "-1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+ |2#is two |2<is more than 2.");
* System.out.println("Formatter Pattern : " + fmt.toPattern());
* *
* System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY)); * System.out.println(fmt.format(Double.NEGATIVE_INFINITY)); // outputs "is negative"
* System.out.println("Format with -1.0 : " + fmt.format(-1.0)); * System.out.println(fmt.format(-1.0)); // outputs "is negative"
* System.out.println("Format with 0 : " + fmt.format(0)); * System.out.println(fmt.format(0)); // outputs "is zero or fraction"
* System.out.println("Format with 0.9 : " + fmt.format(0.9)); * System.out.println(fmt.format(0.9)); // outputs "is zero or fraction"
* System.out.println("Format with 1.0 : " + fmt.format(1)); * System.out.println(fmt.format(1)); // outputs "is one"
* System.out.println("Format with 1.5 : " + fmt.format(1.5)); * System.out.println(fmt.format(1.5)); // outputs "is 1+"
* System.out.println("Format with 2 : " + fmt.format(2)); * System.out.println(fmt.format(2)); // outputs "is two"
* System.out.println("Format with 2.1 : " + fmt.format(2.1)); * System.out.println(fmt.format(2.1)); // outputs "is more than 2."
* System.out.println("Format with NaN : " + fmt.format(Double.NaN)); * System.out.println(fmt.format(Double.NaN)); // outputs "is negative"
* System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY)); * System.out.println(fmt.format(Double.POSITIVE_INFINITY)); // outputs "is more than 2."
* } * }
* </blockquote> * </blockquote>
* And the output result would be like the following:
* <blockquote>
* <pre>{@code
* Format with -INF : is negative
* Format with -1.0 : is negative
* Format with 0 : is zero or fraction
* Format with 0.9 : is zero or fraction
* Format with 1.0 : is one
* Format with 1.5 : is 1+
* Format with 2 : is two
* Format with 2.1 : is more than 2.
* Format with NaN : is negative
* Format with +INF : is more than 2.
* }</pre>
* </blockquote>
* *
* <h2><a id="synchronization">Synchronization</a></h2> * <h2><a id="synchronization">Synchronization</a></h2>
* *
@ -172,12 +227,16 @@ public class ChoiceFormat extends NumberFormat {
private static final long serialVersionUID = 1795184449645032964L; private static final long serialVersionUID = 1795184449645032964L;
/** /**
* Sets the pattern. * Apply the given pattern to this ChoiceFormat object. The syntax
* @param newPattern See the class description. * for the ChoiceFormat pattern can be seen in the {@linkplain ##patterns
* Patterns} section.
*
* @param newPattern a pattern string
* @throws NullPointerException if {@code newPattern} * @throws NullPointerException if {@code newPattern}
* is {@code null} * is {@code null}
* @throws IllegalArgumentException if {@code newPattern} * @throws IllegalArgumentException if {@code newPattern}
* is invalid * violates the pattern syntax
* @see #ChoiceFormat(String)
*/ */
public void applyPattern(String newPattern) { public void applyPattern(String newPattern) {
StringBuilder[] segments = new StringBuilder[2]; StringBuilder[] segments = new StringBuilder[2];
@ -260,9 +319,14 @@ public class ChoiceFormat extends NumberFormat {
} }
/** /**
* Gets the pattern. * {@return a pattern {@code string} that represents the the limits and formats
* of this ChoiceFormat object}
* *
* @return the pattern string * The {@code string} returned is not guaranteed to be the same input
* {@code string} passed to either {@link #applyPattern(String)} or
* {@link #ChoiceFormat(String)}.
*
* @see #applyPattern(String)
*/ */
public String toPattern() { public String toPattern() {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
@ -312,13 +376,16 @@ public class ChoiceFormat extends NumberFormat {
} }
/** /**
* Constructs with limits and corresponding formats based on the pattern. * Constructs a ChoiceFormat with limits and corresponding formats
* based on the pattern.
* The syntax for the ChoiceFormat pattern can be seen in the {@linkplain
* ##patterns Patterns} section.
* *
* @param newPattern the new pattern string * @param newPattern the new pattern string
* @throws NullPointerException if {@code newPattern} is * @throws NullPointerException if {@code newPattern} is
* {@code null} * {@code null}
* @throws IllegalArgumentException if {@code newPattern} * @throws IllegalArgumentException if {@code newPattern}
* is invalid * violates the pattern syntax
* @see #applyPattern * @see #applyPattern
*/ */
public ChoiceFormat(String newPattern) { public ChoiceFormat(String newPattern) {
@ -364,8 +431,7 @@ public class ChoiceFormat extends NumberFormat {
} }
/** /**
* Get the limits passed in the constructor. * {@return the limits of this ChoiceFormat}
* @return the limits.
*/ */
public double[] getLimits() { public double[] getLimits() {
double[] newLimits = Arrays.copyOf(choiceLimits, choiceLimits.length); double[] newLimits = Arrays.copyOf(choiceLimits, choiceLimits.length);
@ -373,8 +439,7 @@ public class ChoiceFormat extends NumberFormat {
} }
/** /**
* Get the formats passed in the constructor. * {@return the formats of this ChoiceFormat}
* @return the formats.
*/ */
public Object[] getFormats() { public Object[] getFormats() {
Object[] newFormats = Arrays.copyOf(choiceFormats, choiceFormats.length); Object[] newFormats = Arrays.copyOf(choiceFormats, choiceFormats.length);
@ -390,6 +455,7 @@ public class ChoiceFormat extends NumberFormat {
* the range that can be stored by double. This will never be * the range that can be stored by double. This will never be
* a practical limitation. * a practical limitation.
*/ */
@Override
public StringBuffer format(long number, StringBuffer toAppendTo, public StringBuffer format(long number, StringBuffer toAppendTo,
FieldPosition status) { FieldPosition status) {
return format((double)number, toAppendTo, status); return format((double)number, toAppendTo, status);
@ -403,7 +469,8 @@ public class ChoiceFormat extends NumberFormat {
* @throws NullPointerException if {@code toAppendTo} * @throws NullPointerException if {@code toAppendTo}
* is {@code null} * is {@code null}
*/ */
public StringBuffer format(double number, StringBuffer toAppendTo, @Override
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition status) { FieldPosition status) {
// find the number // find the number
int i; int i;
@ -434,6 +501,7 @@ public class ChoiceFormat extends NumberFormat {
* or if {@code text} is {@code null} and the list of * or if {@code text} is {@code null} and the list of
* choice strings is not empty. * choice strings is not empty.
*/ */
@Override
public Number parse(String text, ParsePosition status) { public Number parse(String text, ParsePosition status) {
// find the best number (defined as the one with the longest parse) // find the best number (defined as the one with the longest parse)
int start = status.index; int start = status.index;
@ -493,6 +561,7 @@ public class ChoiceFormat extends NumberFormat {
/** /**
* Overrides Cloneable * Overrides Cloneable
*/ */
@Override
public Object clone() public Object clone()
{ {
ChoiceFormat other = (ChoiceFormat) super.clone(); ChoiceFormat other = (ChoiceFormat) super.clone();