ChoiceFormatpublic 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.
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. |
Fields Summary |
---|
private static final long | serialVersionUID | private double[] | choiceLimitsA 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[] | choiceFormatsA 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.
applyPattern(newPattern);
| public ChoiceFormat(double[] limits, String[] formats)Constructs with the limits and the corresponding formats.
setChoices(limits, formats);
|
Methods Summary |
---|
public void | applyPattern(java.lang.String newPattern)Sets the pattern.
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.Object | clone()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 boolean | equals(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.StringBuffer | format(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.StringBuffer | format(double number, java.lang.StringBuffer toAppendTo, java.text.FieldPosition status)Returns pattern with formatted double.
// 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 choiceFormats;
| public double[] | getLimits()Get the limits passed in the constructor.
return choiceLimits;
| public int | hashCode()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 double | nextDouble(double d)Finds the least double greater than d.
If NaN, returns same value.
Used to make half-open intervals.
return nextDouble(d,true);
| public static double | nextDouble(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.Number | parse(java.lang.String text, java.text.ParsePosition status)Parses a Number from the input text.
// 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 double | previousDouble(double d)Finds the greatest double less than d.
If NaN, returns same value.
return nextDouble(d,false);
| private void | readObject(java.io.ObjectInputStream in)After reading an object from the input stream, do a simple verification
to maintain class invariants.
in.defaultReadObject();
if (choiceLimits.length != choiceFormats.length) {
throw new InvalidObjectException(
"limits and format arrays of different length.");
}
| public void | setChoices(double[] limits, java.lang.String[] formats)Set the choices to be used in formatting.
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.String | toPattern()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();
|
|