package ca.sqlpower.util;
import java.text.Format;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.NumberFormat;
/**
* The OrdinalNumberFormat takes regular integers and outputs them as
* strings with the correct English ordinal suffix appended. For
* example, given <code>Format f = new OrdinalNumberFormat()</code>,
*
* <ul>
* <li><code>f.format(1)</code> returns "1st"
* <li><code>f.format(2)</code> returns "2nd"
* <li><code>f.format(111)</code> returns "111th"
* </ul>
*
* <p>TODO: Support French and Spanish locales; support parsing; make
* this a subclass of NumberFormat.
*/
public class OrdinalNumberFormat extends Format {
/**
* This is the FieldPosition id for the number part of the
* formatted output.
*/
public static final int INTEGER_FIELD = NumberFormat.INTEGER_FIELD;
/**
* This is the FieldPosition id for the non-number part of the
* formatted output.
*/
public static final int SUFFIX_FIELD = NumberFormat.INTEGER_FIELD + 1;
/**
* Appends the ordinal representation of the given object to the
* given StringBuffer. See the class-level documentation for
* examples.
*
* @param num The number to format.
* @param toAppendTo The formatted result will be concatenated to
* this StringBuffer.
* @param pos Will be updated to indicate the position of the
* numeric (INTEGER_FIELD) or non-numeric (SUFFIX_FIELD) portion
* of the formatted string. If any position id other than
* SUFFIX_FIELD or INTEGER_FIELD is specified, pos will remain
* untouched.
*/
public StringBuffer format(int num, StringBuffer toAppendTo, FieldPosition pos) {
int startPos = toAppendTo.length();
// build the formatted string
switch(num % 100) {
case 11:
case 12:
case 13:
toAppendTo.append(num).append("th");
break;
default:
switch(num % 10) {
case 1:
toAppendTo.append(num).append("st");
break;
case 2:
toAppendTo.append(num).append("nd");
break;
case 3:
toAppendTo.append(num).append("rd");
break;
default:
toAppendTo.append(num).append("th");
break;
}
}
// update the FieldPosition with requested data
switch (pos.getField()) {
case INTEGER_FIELD:
pos.setBeginIndex(startPos);
pos.setEndIndex(toAppendTo.length());
break;
case SUFFIX_FIELD:
pos.setBeginIndex(toAppendTo.length() - 2);
pos.setEndIndex(toAppendTo.length());
break;
default:
break;
}
return toAppendTo;
}
/**
* Formats the given number to the given StringBuffer. See {@link
* #format(int,StringBuffer,FieldPosition)}.
*/
public StringBuffer format(int num, StringBuffer toAppendTo) {
final FieldPosition dummyPos = new FieldPosition(INTEGER_FIELD-1);
return format(num, toAppendTo, dummyPos);
}
/**
* Formats the given number to a new StringBuffer. See {@link
* #format(int,StringBuffer,FieldPosition)}.
*/
public StringBuffer format(int num) {
return format(num, new StringBuffer((int) (Math.log(num)/Math.log(10))+3));
}
/**
* See {@link #format(int,StringBuffer,FieldPosition)}. The only
* difference is that obj is the number to format, and it has to
* be a subclass of Number.
*/
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
if (! (obj instanceof Number)) {
throw new IllegalArgumentException
("OrdinalNumberFormat can only format subclasses of Number");
}
return format(((Number) obj).intValue(), toAppendTo, pos);
}
/**
* Not implemented.
*/
public Object parseObject(String source, ParsePosition pos) {
throw new UnsupportedOperationException("Parsing not implemented");
}
}