ProblemYou need to format numbers as Roman numerals. Perhaps you've just written the next Titanic or Star Wars episode and you need to get the copyright date correct. Or, on a more mundane level, you need to format page numbers in the front matter of a book. SolutionUse my RomanNumberFormat class: // RomanNumberSimple.java RomanNumberFormat nf = new RomanNumberFormat( ); int year = Calendar.getInstance( ).get(Calendar.YEAR); System.out.println(year + " -> " + nf.format(year)); The use of Calendar to get the current year is explained in Recipe 6.1. Running RomanNumberSimple looks like this: + jikes +E -d . RomanNumberSimple.java + java RomanNumberSimple 2004 -> MMIV DiscussionNothing in the standard API formats Roman numerals. However, the java.text.Format class is designed to be subclassed for precisely such unanticipated purposes, so I have done just that and developed a class to format numbers as Roman numerals. Here is a better and complete example program of using it to format the current year. I can pass a number of arguments on the command line, including a "-" where I want the year to appear (note that these arguments are normally not quoted; the "-" must be an argument all by itself, just to keep the program simple). I use it as follows: $ java RomanYear Copyright (c) - Ian Darwin Copyright (c) MMIV Ian Darwin $ The code for the RomanYear program is simple, yet it correctly puts spaces around the arguments: import java.util.*; /** Print the current year in Roman Numerals */ public class RomanYear { public static void main(String[] argv) { RomanNumberFormat rf = new RomanNumberFormat( ); Calendar cal = Calendar.getInstance( ); int year = cal.get(Calendar.YEAR); // If no arguments, just print the year. if (argv.length == 0) { System.out.println(rf.format(year)); return; } // Else a micro-formatter: replace "-" arg with year, else print. for (int i=0; i<argv.length; i++) { if (argv[i].equals("-")) System.out.print(rf.format(year)); else System.out.print(argv[i]); // e.g., "Copyright" System.out.print(' '); } System.out.println( ); } } Now here's the code for the RomanNumberFormat class. I did sneak in one additional class, java.text.FieldPosition . A FieldPosition simply represents the position of one numeric field in a string that has been formatted using a variant of NumberFormat.format( ). You construct it to represent either the integer part or the fraction part (of course, Roman numerals don't have fractional parts). The FieldPosition methods getBeginIndex( ) and getEndIndex( ) indicate where in the resulting string the given field wound up. Example 5-2 is the class that implements Roman number formatting. As the comments indicate, the one limitation is that the input number must be less than 4,000. Example 5-2. RomanNumberFormat.javaimport java.text.*; import java.util.*; /** * Roman Number class. Not localized, since Latin's a Dead Dead Language * and we don't display Roman Numbers differently in different Locales. * Filled with quick-n-dirty algorithms. */ public class RomanNumberFormat extends Format { /** Characters used in "Arabic to Roman", that is, format( ) methods. */ static char A2R[][] = { { 0, 'M' }, { 0, 'C', 'D', 'M' }, { 0, 'X', 'L', 'C' }, { 0, 'I', 'V', 'X' }, }; /** Format a given double as a Roman Numeral; just truncate to a * long, and call format(long). */ public String format(double n) { return format((long)n); } /** Format a given long as a Roman Numeral. Just call the * three-argument form. */ public String format(long n) { if (n <= 0 || n >= 4000) throw new IllegalArgumentException(n + " must be > 0 && < 4000"); StringBuffer sb = new StringBuffer( ); format(new Integer((int)n), sb, new FieldPosition(NumberFormat.INTEGER return sb.toString( ); } /* Format the given Number as a Roman Numeral, returning the * Stringbuffer (updated), and updating the FieldPosition. * This method is the REAL FORMATTING ENGINE. * Method signature is overkill, but required as a subclass of Format. */ public StringBuffer format(Object on, StringBuffer sb, FieldPosition fp) { if (!(on instanceof Number)) throw new IllegalArgumentException(on + " must be a Number object"); if (fp.getField( ) != NumberFormat.INTEGER_FIELD) throw new IllegalArgumentException(fp + int n = ((Number)on).intValue( ); // TODO check for in range here // First, put the digits on a tiny stack. Must be 4 digits. for (int i=0; i<4; i++) { int d=n%10; push(d); // System.out.println("Pushed " + d); n=n/10; } // Now pop and convert. for (int i=0; i<4; i++) { int ch = pop( ); // System.out.println("Popped " + ch); if (ch==0) continue; else if (ch <= 3) { for(int k=1; k<=ch; k++) sb.append(A2R[i][1]); // I } else if (ch == 4) { sb.append(A2R[i][1]); // I sb.append(A2R[i][2]); // V } else if (ch == 5) { sb.append(A2R[i][2]); // V } else if (ch <= 8) { sb.append(A2R[i][2]); // V for (int k=6; k<=ch; k++) sb.append(A2R[i][1]); // I } else { // 9 sb.append(A2R[i][1]); sb.append(A2R[i][3]); } } // fp.setBeginIndex(0); // fp.setEndIndex(3); return sb; } /** Parse a generic object, returning an Object */ public Object parseObject(String what, ParsePosition where) { throw new IllegalArgumentException("Parsing not implemented"); // TODO PARSING HERE // return new Long(0); } /* Implement a toy stack */ protected int stack[] = new int[10]; protected int depth = 0; /* Implement a toy stack */ protected void push(int n) { stack[depth++] = n; } /* Implement a toy stack */ protected int pop( ) { return stack[--depth]; } } Several of the public methods are required because I wanted it to be a subclass of Format, which is abstract. This accounts for some of the complexity, like having three different format methods. Note that the parseObject( ) method is also required, but we don't actually implement parsing in this version. This is left as the usual exercise for the reader. See AlsoJava I/O (O'Reilly) has an entire chapter on NumberFormat and develops an ExponentialNumberFormat subclass. The online source for this book has ScaledNumberFormat , which prints numbers with a maximum of four digits and a computerish scale factor (B for bytes, K for kilo-, M for mega-, and so on). |