FileDocCategorySizeDatePackage
ChoiceFormat.javaAPI DocJava SE 5 API22119Fri Aug 26 14:57:20 BST 2005java.text

ChoiceFormat

public class ChoiceFormat extends NumberFormat
A ChoiceFormat allows you to attach a format to a range of numbers. It is generally used in a 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:
X matches j if and only if limit[j] <= X < limit[j+1]
If there is no match, then either the first or last index is used, depending on whether the number (X) is too low or too high. If the limit array is not in ascending order, the results of formatting will be incorrect. ChoiceFormat also accepts \u221E as equivalent to infinity(INF).

Note: ChoiceFormat differs from the other Format classes in that you create a ChoiceFormat object with a constructor (not with a getInstance style factory method). The factory methods aren't necessary because ChoiceFormat doesn't require any complex setup for a given locale. In fact, ChoiceFormat doesn't implement any locale specific behavior.

When creating a ChoiceFormat, you must specify an array of formats and an array of limits. The length of these arrays must be the same. For example,

  • limits = {1,2,3,4,5,6,7}
    formats = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
  • limits = {0, 1, ChoiceFormat.nextDouble(1)}
    formats = {"no files", "one file", "many files"}
    (nextDouble can be used to get the next higher double, to make the half-open interval.)

Here is a simple example that shows formatting and parsing:

double[] limits = {1,2,3,4,5,6,7};
String[] monthNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
ChoiceFormat form = new ChoiceFormat(limits, monthNames);
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));
}
Here is a more complex example, with a pattern format:
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] = new Integer(i);
testArgs[2] = testArgs[0];
System.out.println(pattform.format(testArgs));
}

Specifying a pattern for ChoiceFormat objects is fairly straightforward. For example:

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));
And the output result would be like the following:
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.

Synchronization

Choice formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

see
DecimalFormat
see
MessageFormat
version
1.22 09/21/98
author
Mark Davis

Fields Summary
private static final long
serialVersionUID
private double[]
choiceLimits
A list of lower bounds for the choices. The formatter will return choiceFormats[i] if the number being formatted is greater than or equal to choiceLimits[i] and less than choiceLimits[i+1].
private String[]
choiceFormats
A list of choice strings. The formatter will return choiceFormats[i] if the number being formatted is greater than or equal to choiceLimits[i] and less than choiceLimits[i+1].
static final long
SIGN
static final long
EXPONENT
static final long
POSITIVEINFINITY
Constructors Summary
public ChoiceFormat(String newPattern)
Constructs with limits and corresponding formats based on the pattern.

see
#applyPattern

        applyPattern(newPattern);
    
public ChoiceFormat(double[] limits, String[] formats)
Constructs with the limits and the corresponding formats.

see
#setChoices

        setChoices(limits, formats);
    
Methods Summary
public voidapplyPattern(java.lang.String newPattern)
Sets the pattern.

param
newPattern See the class description.


                  
        
        StringBuffer[] segments = new StringBuffer[2];
        for (int i = 0; i < segments.length; ++i) {
            segments[i] = new StringBuffer();
        }
        double[] newChoiceLimits = new double[30];
        String[] newChoiceFormats = new String[30];
        int count = 0;
        int part = 0;
        double startValue = 0;
        double oldStartValue = Double.NaN;
        boolean inQuote = false;
        for (int i = 0; i < newPattern.length(); ++i) {
            char ch = newPattern.charAt(i);
            if (ch=='\'") {
                // Check for "''" indicating a literal quote
                if ((i+1)<newPattern.length() && newPattern.charAt(i+1)==ch) {
                    segments[part].append(ch);
                    ++i;
                } else {
                    inQuote = !inQuote;
                }
            } else if (inQuote) {
                segments[part].append(ch);
            } else if (ch == '<" || ch == '#" || ch == '\u2264") {
                if (segments[0].equals("")) {
                    throw new IllegalArgumentException();
                }
                try {
                    String tempBuffer = segments[0].toString();
                    if (tempBuffer.equals("\u221E")) {
                        startValue = Double.POSITIVE_INFINITY;
                    } else if (tempBuffer.equals("-\u221E")) {
                        startValue = Double.NEGATIVE_INFINITY;
                    } else {
                        startValue = Double.valueOf(segments[0].toString()).doubleValue();
                    }
                } catch (Exception e) {
                    throw new IllegalArgumentException();
                }
                if (ch == '<" && startValue != Double.POSITIVE_INFINITY &&
                        startValue != Double.NEGATIVE_INFINITY) {
                    startValue = nextDouble(startValue);
                }
                if (startValue <= oldStartValue) {
                    throw new IllegalArgumentException();
                }
                segments[0].setLength(0);
                part = 1;
            } else if (ch == '|") {
                if (count == newChoiceLimits.length) {
                    newChoiceLimits = doubleArraySize(newChoiceLimits);
                    newChoiceFormats = doubleArraySize(newChoiceFormats);
                }
                newChoiceLimits[count] = startValue;
                newChoiceFormats[count] = segments[1].toString();
                ++count;
                oldStartValue = startValue;
                segments[1].setLength(0);
                part = 0;
            } else {
                segments[part].append(ch);
            }
        }
        // clean up last one
        if (part == 1) {
            if (count == newChoiceLimits.length) {
                newChoiceLimits = doubleArraySize(newChoiceLimits);
                newChoiceFormats = doubleArraySize(newChoiceFormats);
            }
            newChoiceLimits[count] = startValue;
            newChoiceFormats[count] = segments[1].toString();
            ++count;
        }
        choiceLimits = new double[count];
        System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
        choiceFormats = new String[count];
        System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
    
public java.lang.Objectclone()
Overrides Cloneable

        ChoiceFormat other = (ChoiceFormat) super.clone();
        // for primitives or immutables, shallow clone is enough
        other.choiceLimits = (double[]) choiceLimits.clone();
        other.choiceFormats = (String[]) choiceFormats.clone();
        return other;
    
private static double[]doubleArraySize(double[] array)

        int oldSize = array.length;
        double[] newArray = new double[oldSize * 2];
        System.arraycopy(array, 0, newArray, 0, oldSize);
        return newArray;
    
private java.lang.String[]doubleArraySize(java.lang.String[] array)

        int oldSize = array.length;
        String[] newArray = new String[oldSize * 2];
        System.arraycopy(array, 0, newArray, 0, oldSize);
        return newArray;
    
public booleanequals(java.lang.Object obj)
Equality comparision between two

        if (obj == null) return false;
        if (this == obj)                      // quick check
            return true;
        if (getClass() != obj.getClass())
            return false;
        ChoiceFormat other = (ChoiceFormat) obj;
        return (Utility.arrayEquals(choiceLimits,other.choiceLimits)
            && Utility.arrayEquals(choiceFormats,other.choiceFormats));
    
public java.lang.StringBufferformat(long number, java.lang.StringBuffer toAppendTo, java.text.FieldPosition status)
Specialization of format. This method really calls format(double, StringBuffer, FieldPosition) thus the range of longs that are supported is only equal to the range that can be stored by double. This will never be a practical limitation.

        return format((double)number, toAppendTo, status);
    
public java.lang.StringBufferformat(double number, java.lang.StringBuffer toAppendTo, java.text.FieldPosition status)
Returns pattern with formatted double.

param
number number to be formatted & substituted.
param
toAppendTo where text is appended.
param
status ignore no useful status is returned.

        // find the number
        int i;
        for (i = 0; i < choiceLimits.length; ++i) {
            if (!(number >= choiceLimits[i])) {
                // same as number < choiceLimits, except catchs NaN
                break;
            }
        }
        --i;
        if (i < 0) i = 0;
        // return either a formatted number, or a string
        return toAppendTo.append(choiceFormats[i]);
    
public java.lang.Object[]getFormats()
Get the formats passed in the constructor.

return
the formats.

        return choiceFormats;
    
public double[]getLimits()
Get the limits passed in the constructor.

return
the limits.

        return choiceLimits;
    
public inthashCode()
Generates a hash code for the message format object.

        int result = choiceLimits.length;
        if (choiceFormats.length > 0) {
            // enough for reasonable distribution
            result ^= choiceFormats[choiceFormats.length-1].hashCode();
        }
        return result;
    
public static final doublenextDouble(double d)
Finds the least double greater than d. If NaN, returns same value.

Used to make half-open intervals.

see
#previousDouble

        return nextDouble(d,true);
    
public static doublenextDouble(double d, boolean positive)
Finds the least double greater than d (if positive == true), or the greatest double less than d (if positive == false). If NaN, returns same value. Does not affect floating-point flags, provided these member functions do not: Double.longBitsToDouble(long) Double.doubleToLongBits(double) Double.isNaN(double)


                                                                             
            

        /* filter out NaN's */
        if (Double.isNaN(d)) {
            return d;
        }

        /* zero's are also a special case */
        if (d == 0.0) {
            double smallestPositiveDouble = Double.longBitsToDouble(1L);
            if (positive) {
                return smallestPositiveDouble;
            } else {
                return -smallestPositiveDouble;
            }
        }

        /* if entering here, d is a nonzero value */

        /* hold all bits in a long for later use */
        long bits = Double.doubleToLongBits(d);

        /* strip off the sign bit */
        long magnitude = bits & ~SIGN;

        /* if next double away from zero, increase magnitude */
        if ((bits > 0) == positive) {
            if (magnitude != POSITIVEINFINITY) {
                magnitude += 1;
            }
        }
        /* else decrease magnitude */
        else {
            magnitude -= 1;
        }

        /* restore sign bit and return */
        long signbit = bits & SIGN;
        return Double.longBitsToDouble (magnitude | signbit);
    
public java.lang.Numberparse(java.lang.String text, java.text.ParsePosition status)
Parses a Number from the input text.

param
text the source text.
param
status an input-output parameter. On input, the status.index field indicates the first character of the source text that should be parsed. On exit, if no error occured, status.index is set to the first unparsed character in the source text. On exit, if an error did occur, status.index is unchanged and status.errorIndex is set to the first index of the character that caused the parse to fail.
return
A Number representing the value of the number parsed.

        // find the best number (defined as the one with the longest parse)
        int start = status.index;
        int furthest = start;
        double bestNumber = Double.NaN;
        double tempNumber = 0.0;
        for (int i = 0; i < choiceFormats.length; ++i) {
            String tempString = choiceFormats[i];
            if (text.regionMatches(start, tempString, 0, tempString.length())) {
                status.index = start + tempString.length();
                tempNumber = choiceLimits[i];
                if (status.index > furthest) {
                    furthest = status.index;
                    bestNumber = tempNumber;
                    if (furthest == text.length()) break;
                }
            }
        }
        status.index = furthest;
        if (status.index == start) {
            status.errorIndex = furthest;
        }
        return new Double(bestNumber);
    
public static final doublepreviousDouble(double d)
Finds the greatest double less than d. If NaN, returns same value.

see
#nextDouble

        return nextDouble(d,false);
    
private voidreadObject(java.io.ObjectInputStream in)
After reading an object from the input stream, do a simple verification to maintain class invariants.

throws
InvalidObjectException if the objects read from the stream is invalid.

        in.defaultReadObject();
        if (choiceLimits.length != choiceFormats.length) {
            throw new InvalidObjectException(
                    "limits and format arrays of different length.");
        }
    
public voidsetChoices(double[] limits, java.lang.String[] formats)
Set the choices to be used in formatting.

param
limits contains the top value that you want parsed with that format,and should be in ascending sorted order. When formatting X, the choice will be the i, where limit[i] <= X < limit[i+1]. If the limit array is not in ascending order, the results of formatting will be incorrect.
param
formats are the formats you want to use for each limit. They can be either Format objects or Strings. When formatting with object Y, if the object is a NumberFormat, then ((NumberFormat) Y).format(X) is called. Otherwise Y.toString() is called.

        if (limits.length != formats.length) {
            throw new IllegalArgumentException(
                "Array and limit arrays must be of the same length.");
        }
        choiceLimits = limits;
        choiceFormats = formats;
    
public java.lang.StringtoPattern()
Gets the pattern.

        StringBuffer result = new StringBuffer();
        for (int i = 0; i < choiceLimits.length; ++i) {
            if (i != 0) {
                result.append('|");
            }
            // choose based upon which has less precision
            // approximate that by choosing the closest one to an integer.
            // could do better, but it's not worth it.
            double less = previousDouble(choiceLimits[i]);
            double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
            double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));

            if (tryLessOrEqual < tryLess) {
                result.append(""+choiceLimits[i]);
                result.append('#");
            } else {
                if (choiceLimits[i] == Double.POSITIVE_INFINITY) {
                    result.append("\u221E");
                } else if (choiceLimits[i] == Double.NEGATIVE_INFINITY) {
                    result.append("-\u221E");
                } else {
                    result.append(""+less);
                }
                result.append('<");
            }
            // Append choiceFormats[i], using quotes if there are special characters.
            // Single quotes themselves must be escaped in either case.
            String text = choiceFormats[i];
            boolean needQuote = text.indexOf('<") >= 0
                || text.indexOf('#") >= 0
                || text.indexOf('\u2264") >= 0
                || text.indexOf('|") >= 0;
            if (needQuote) result.append('\'");
            if (text.indexOf('\'") < 0) result.append(text);
            else {
                for (int j=0; j<text.length(); ++j) {
                    char c = text.charAt(j);
                    result.append(c);
                    if (c == '\'") result.append(c);
                }
            }
            if (needQuote) result.append('\'");
        }
        return result.toString();