Skip to content

Commit

Permalink
6228794: java.text.ChoiceFormat pattern behavior is not well documented.
Browse files Browse the repository at this point in the history
Reviewed-by: naoto
  • Loading branch information
Justin Lu committed Sep 13, 2023
1 parent 3b0a6d2 commit ce93d27
Showing 1 changed file with 116 additions and 47 deletions.
163 changes: 116 additions & 47 deletions src/java.base/share/classes/java/text/ChoiceFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
import java.util.Arrays;

/**
* A {@code ChoiceFormat} allows you to attach a format to a range of numbers.
* It is generally used in a {@code MessageFormat} for handling plurals.
* {@code ChoiceFormat} is a concrete subclass of {@code NumberFormat} that
* 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
* specifies a half-open interval up to the next item:
* <blockquote>
Expand All @@ -68,8 +69,10 @@
* {@code ChoiceFormat} doesn't implement any locale specific behavior.
*
* <p>
* When creating a {@code ChoiceFormat}, you must specify an array of formats
* and an array of limits. The length of these arrays must be the same.
* 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>
Expand All @@ -83,7 +86,8 @@
* </ul>
*
* <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>
* {@snippet lang=java :
* double[] limits = {1,2,3,4,5,6,7};
Expand All @@ -97,7 +101,8 @@
* }
* }
* </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>
* {@snippet lang=java :
* double[] filelimits = {0,1,2};
Expand All @@ -114,42 +119,92 @@
* }
* }
* </blockquote>
* <p>
* Specifying a pattern for ChoiceFormat objects is fairly straightforward.
* For example:
* Would output the following:
* <blockquote>
* <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>
* {@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.");
* System.out.println("Formatter Pattern : " + fmt.toPattern());
*
* System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
* System.out.println("Format with -1.0 : " + fmt.format(-1.0));
* System.out.println("Format with 0 : " + fmt.format(0));
* System.out.println("Format with 0.9 : " + fmt.format(0.9));
* System.out.println("Format with 1.0 : " + fmt.format(1));
* System.out.println("Format with 1.5 : " + fmt.format(1.5));
* System.out.println("Format with 2 : " + fmt.format(2));
* System.out.println("Format with 2.1 : " + fmt.format(2.1));
* System.out.println("Format with NaN : " + fmt.format(Double.NaN));
* System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
* System.out.println(fmt.format(Double.NEGATIVE_INFINITY)); // outputs "is negative"
* System.out.println(fmt.format(-1.0)); // outputs "is negative"
* System.out.println(fmt.format(0)); // outputs "is zero or fraction"
* System.out.println(fmt.format(0.9)); // outputs "is zero or fraction"
* System.out.println(fmt.format(1)); // outputs "is one"
* System.out.println(fmt.format(1.5)); // outputs "is 1+"
* System.out.println(fmt.format(2)); // outputs "is two"
* System.out.println(fmt.format(2.1)); // outputs "is more than 2."
* System.out.println(fmt.format(Double.NaN)); // outputs "is negative"
* System.out.println(fmt.format(Double.POSITIVE_INFINITY)); // outputs "is more than 2."
* }
* </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>
*
Expand All @@ -172,12 +227,16 @@ public class ChoiceFormat extends NumberFormat {
private static final long serialVersionUID = 1795184449645032964L;

/**
* Sets the pattern.
* @param newPattern See the class description.
* Apply the given pattern to this ChoiceFormat object. The syntax
* for the ChoiceFormat pattern can be seen in the {@linkplain ##patterns
* Patterns} section.
*
* @param newPattern a pattern string
* @throws NullPointerException if {@code newPattern}
* is {@code null}
* @throws IllegalArgumentException if {@code newPattern}
* is invalid
* violates the pattern syntax
* @see #ChoiceFormat(String)
*/
public void applyPattern(String newPattern) {
StringBuilder[] segments = new StringBuilder[2];
Expand Down Expand Up @@ -260,9 +319,14 @@ public void applyPattern(String newPattern) {
}

/**
* 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() {
StringBuilder result = new StringBuilder();
Expand Down Expand Up @@ -312,13 +376,16 @@ public String toPattern() {
}

/**
* 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
* @throws NullPointerException if {@code newPattern} is
* {@code null}
* @throws IllegalArgumentException if {@code newPattern}
* is invalid
* violates the pattern syntax
* @see #applyPattern
*/
public ChoiceFormat(String newPattern) {
Expand Down Expand Up @@ -364,17 +431,15 @@ public void setChoices(double[] limits, String[] formats) {
}

/**
* Get the limits passed in the constructor.
* @return the limits.
* {@return the limits of this ChoiceFormat}
*/
public double[] getLimits() {
double[] newLimits = Arrays.copyOf(choiceLimits, choiceLimits.length);
return newLimits;
}

/**
* Get the formats passed in the constructor.
* @return the formats.
* {@return the formats of this ChoiceFormat}
*/
public Object[] getFormats() {
Object[] newFormats = Arrays.copyOf(choiceFormats, choiceFormats.length);
Expand All @@ -390,6 +455,7 @@ public Object[] getFormats() {
* the range that can be stored by double. This will never be
* a practical limitation.
*/
@Override
public StringBuffer format(long number, StringBuffer toAppendTo,
FieldPosition status) {
return format((double)number, toAppendTo, status);
Expand All @@ -403,7 +469,8 @@ public StringBuffer format(long number, StringBuffer toAppendTo,
* @throws NullPointerException if {@code toAppendTo}
* is {@code null}
*/
public StringBuffer format(double number, StringBuffer toAppendTo,
@Override
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition status) {
// find the number
int i;
Expand Down Expand Up @@ -434,6 +501,7 @@ public StringBuffer format(double number, StringBuffer toAppendTo,
* or if {@code text} is {@code null} and the list of
* choice strings is not empty.
*/
@Override
public Number parse(String text, ParsePosition status) {
// find the best number (defined as the one with the longest parse)
int start = status.index;
Expand Down Expand Up @@ -493,6 +561,7 @@ public static final double previousDouble (double d) {
/**
* Overrides Cloneable
*/
@Override
public Object clone()
{
ChoiceFormat other = (ChoiceFormat) super.clone();
Expand Down

0 comments on commit ce93d27

Please sign in to comment.