java.text
public class: ChoiceFormat [javadoc |
source]
java.lang.Object
java.text.Format
java.text.NumberFormat
java.text.ChoiceFormat
All Implemented Interfaces:
Cloneable, Serializable
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[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
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.
Field Summary |
---|
static final long | SIGN | |
static final long | EXPONENT | |
static final long | POSITIVEINFINITY | |
Method from java.text.ChoiceFormat Summary: |
---|
applyPattern, clone, equals, format, format, getFormats, getLimits, hashCode, nextDouble, nextDouble, parse, previousDouble, setChoices, toPattern |
Methods from java.text.NumberFormat: |
---|
clone, equals, format, format, format, format, format, getAvailableLocales, getCurrency, getCurrencyInstance, getCurrencyInstance, getInstance, getInstance, getIntegerInstance, getIntegerInstance, getMaximumFractionDigits, getMaximumIntegerDigits, getMinimumFractionDigits, getMinimumIntegerDigits, getNumberInstance, getNumberInstance, getPercentInstance, getPercentInstance, getRoundingMode, getScientificInstance, getScientificInstance, hashCode, isGroupingUsed, isParseIntegerOnly, parse, parse, parseObject, setCurrency, setGroupingUsed, setMaximumFractionDigits, setMaximumIntegerDigits, setMinimumFractionDigits, setMinimumIntegerDigits, setParseIntegerOnly, setRoundingMode |
Methods from java.lang.Object: |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Method from java.text.ChoiceFormat Detail: |
public void applyPattern(String newPattern) {
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].length() == 0) {
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 Object clone() {
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;
}
|
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) // quick check
return true;
if (getClass() != obj.getClass())
return false;
ChoiceFormat other = (ChoiceFormat) obj;
return (Arrays.equals(choiceLimits, other.choiceLimits)
&& Arrays.equals(choiceFormats, other.choiceFormats));
}
Equality comparision between two |
public StringBuffer format(long number,
StringBuffer toAppendTo,
FieldPosition status) {
return format((double)number, toAppendTo, 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. |
public StringBuffer format(double number,
StringBuffer toAppendTo,
FieldPosition status) {
// 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]);
}
Returns pattern with formatted double. |
public Object[] getFormats() {
return choiceFormats;
}
Get the formats passed in the constructor. |
public double[] getLimits() {
return choiceLimits;
}
Get the limits passed in the constructor. |
public int hashCode() {
int result = choiceLimits.length;
if (choiceFormats.length > 0) {
// enough for reasonable distribution
result ^= choiceFormats[choiceFormats.length-1].hashCode();
}
return result;
}
Generates a hash code for the message format object. |
public static final double nextDouble(double d) {
return nextDouble(d,true);
}
|
public static double nextDouble(double d,
boolean positive) {
/* 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);
}
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) |
public Number parse(String text,
ParsePosition status) {
// 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);
}
Parses a Number from the input text. |
public static final double previousDouble(double d) {
return nextDouble(d,false);
}
Finds the greatest double less than d.
If NaN, returns same value. |
public void setChoices(double[] limits,
String[] formats) {
if (limits.length != formats.length) {
throw new IllegalArgumentException(
"Array and limit arrays must be of the same length.");
}
choiceLimits = limits;
choiceFormats = formats;
}
Set the choices to be used in formatting. |
public String toPattern() {
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();
}
|