Formatting with the java.text Package

The java.text package includes, among other things, a set of classes designed for generating and parsing string representations of objects. In this section, we'll talk about three classes: NumberFormat, ChoiceFormat, and MessageFormat. describes the DateFormat class. As we said earlier, the classes of the java.text package overlap to a large degree with the capabilities of the Scanner and printf-style Formatter in Java 5.0. Despite these new features, a number of areas in the parsing of currencies, dates, and times can only be handled with the java.text package. The NumberFormat class can be used to format and parse currency, percentages, or plain old numbers. NumberFormat is an abstract class, but it has several useful factory methods that produce formatters for different types of numbers. For example, to format or parse currency strings, use getCurrencyInstance( ):

 double salary = 1234.56;
 String here = // $1,234.56
 NumberFormat.getCurrencyInstance( ).format(salary);
 String italy = // L 1.234,56
 NumberFormat.getCurrencyInstance(Locale.ITALY).format(salary);


The first statement generates an American salary, with a dollar sign, a comma to separate thousands, and a period as a decimal point. The second statement presents the same string in Italian, with a lire sign, a period to separate thousands, and a comma as a decimal point. Remember that NumberFormat worries about format only; it doesn't attempt to do currency conversion. (That would require, among other things, access to a dynamically updated table of exchange ratesa good opportunity for a Java bean but too much to ask of a simple formatter.) We can go the other way and parse a formatted value using the parse( ) method, as we'll see in the next example. Likewise, getPercentInstance( ) returns a formatter you can use for generating and parsing percentages. If you do not specify a Locale when calling a getInstance( ) method, the default Locale is used:

 double progress = 0.44;
 NumberFormat pf = NumberFormat.getPercentInstance( );
 System.out.println( pf.format(progress) ); // "44%"
 try {
 System.out.println( pf.parse("77.2%") ); // "0.772"
 }
 catch (ParseException e) {}


And if you just want to generate and parse plain old numbers, use a NumberFormat returned by getInstance( ) or its equivalent, getNumberInstance( ):

 NumberFormat guiseppe = NumberFormat.getInstance(Locale.ITALY);
 // defaults to Locale.US
 NumberFormat joe = NumberFormat.getInstance( );
 try {
 double theValue = guiseppe.parse("34.663,252").doubleValue( );
 System.out.println(joe.format(theValue)); // "34,663.252"
 }
 catch (ParseException e) {}


We use guiseppe to parse a number in Italian format (periods separate thousands, comma is the decimal point). The return type of parse( ) is Number, so we use the doubleValue( ) method to retrieve the value of the Number as a double. Then we use joe to format the number correctly for the default (U.S.) locale. Here's a list of the factory methods for text formatters in the java.text package. Again, we'll look at the DateFormat methods in the next chapter.

 NumberFormat.getCurrencyInstance( )
 NumberFormat.getCurrencyInstance(Locale inLocale)
 NumberFormat.getInstance( )
 NumberFormat.getInstance(Locale inLocale)
 NumberFormat.getNumberInstance( )
 NumberFormat.getNumberInstance(Locale inLocale)
 NumberFormat.getPercentInstance( )
 NumberFormat.getPercentInstance(Locale inLocale)
 DateFormat.getDateInstance( )
 DateFormat.getDateInstance(int style)
 DateFormat.getDateInstance(int style, Locale aLocale)
 DateFormat.getDateTimeInstance( )
 DateFormat.getDateTimeInstance(int dateStyle, int timeStyle)
 DateFormat.getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
 DateFormat.getInstance( )
 DateFormat.getTimeInstance( )
 DateFormat.getTimeInstance(int style)
 DateFormat.getTimeInstance(int style, Locale aLocale)


Thus far, we've seen how to format numbers as text. Now, we'll take a look at a class, ChoiceFormat, that maps numerical ranges to text. ChoiceFormat is constructed by specifying the numerical ranges and the strings that correspond to them. One constructor accepts an array of doubles and an array of Strings, where each string corresponds to the range running from the matching number up to (but not including) the next number in the array:

 double[] limits = new double [] {0, 20, 40};
 String[] labels = new String [] {"young", "less young", "old"};
 ChoiceFormat cf = new ChoiceFormat(limits, labels);
 System.out.println(cf.format(12)); // young
 System.out.println(cf.format(26)); // less young


You can specify both the limits and the labels using a special string in an alternative ChoiceFormat constructor:

 ChoiceFormat cf = new ChoiceFormat("0#young|20#less young|40#old");
 System.out.println(cf.format(40)); // old
 System.out.println(cf.format(50)); // old


The limit and value pairs are separated by vertical bars (|); the number sign (#) separates each limit from its corresponding value. ChoiceFormat is most useful for handling pluralization in messages, enabling you to avoid hideous constructions such as, "you have one file(s) open." You can create readable error messages by using ChoiceFormat along with the MessageFormat class.

MessageFormat

MessageFormat is a string formatter that uses a pattern string in the same way that printf( ) formatting does. In Java 5.0, MessageFormat is largely replaced by printf( ), which has more options and is more widely used outside of Java. Nonetheless, some may still prefer MessageFormat's style, which is a bit less cryptic than that of printf( ). Prior to Java 5.0, MessageFormat accepted only arrays of arguments for its format method. With the introduction of varargs, it now offers a static formatting method, MessageFormat.format( ), paralleling the print-style formatting of String.format( ). Arguments in a MessageFormat format string are delineated by curly brackets and may include information about how they should be formatted. Each argument consists of a number, an optional type, and an optional style, as summarized in Table 10-8.

Table 10-8. MessageFormat arguments

Type

Styles

Choice

pattern

Date

short, medium, long, full, pattern

Number

integer, percent, currency, pattern

Time

short, medium, long, full, pattern


Let's use an example to clarify this:

 // Java 5.0
 MessageFormat.format("You have {0} messages.", "no");
 // or without varargs, use an Object []
 MessageFormat mf = new MessageFormat("You have {0} messages.");
 Object[] arguments = {"no"};
 System.out.println(mf.format(arguments)); // "You have no messages."


The special incantation {0} means "use element zero of the arguments supplied to the format( ) method." When we generate a message by calling format( ), we pass in values to replace the placeholders ({0}, {1}, ... ) in the template. In this case, we pass the string "no" as arguments[0], yielding the result, You have no messages. Let's try this example again, but this time, we'll format a number and a date instead of a string argument:

 MessageFormat mf = new MessageFormat(
 "You have {0, number, integer} messages on {1, date, long}.");
 Object[] arguments = {new Integer(93), new Date( )};
 // "You have 93 messages on April 10, 2002."
 System.out.println(mf.format(arguments));


In this example, we need to fill in two spaces in the template, so we need two elements in the arguments[] array. Element 0 must be a number and is formatted as an integer. Element 1 must be a Date and is printed in the long format. When we call format( ), the arguments[] array supplies these two values. This is still sloppy. What if there is only one message? To make this grammatically correct, we can embed a ChoiceFormat-style pattern string in our MessageFormat pattern string:

 MessageFormat mf = new MessageFormat(
 "You have {0, number, integer} message{0, choice, 0#s|1#|2#s}.");
 Object[] arguments = {new Integer(1)};
 // "You have 1 message."
 System.out.println(mf.format(arguments));


In this case, we use element 0 of arguments[] twice: once to supply the number of messages and once to provide input to the ChoiceFormat pattern. The pattern says to add an s if argument 0 has the value 0 or is 2 or more. When writing internationalized programs, you can use resource bundles to supply not only the text of messages, but the format strings for your MessageFormat objects, as well. In this way, you can automatically format messages that are in the appropriate language with dates and other language-dependent fields handled appropriately and in the appropriate order. Because arguments in the format string are numbered, you can refer to them in any location. For example, in English, you might say, "Disk C has 123 files"; in some other language, you might say, "123 files are on Disk C." You could implement both messages with the same set of arguments:

 MessageFormat m1 = new MessageFormat(
 "Disk {0} has {1, number, integer} files.");
 MessageFormat m2 = new MessageFormat(
 "{1, number, integer} files are on disk {0}.");
 Object[] arguments = {"C", new Integer(123)};


In real life, the code could be more compact; you'd use only a single MessageFormat object, initialized with a string taken from a resource bundle. Or in Java 5.0, you'd likely want to use the static format method or switch to printf( ) entirely.

Java ScreenShot
Comments