RomanNumeralFormat

Several years ago I wrote this RomanNumeralFormat class which extends java.text.NumberFormat but parses and formats Roman numerals. I just stumbled across it and thought it belonged here. This sort of thing should definitely be included in Java 7, then it will truly rule over Perl 6.

import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;

public class RomanNumeralFormat extends NumberFormat {

  private static char[] roman_alpha = new char[] { 'M', 'D', 'C',
    'L', 'X', 'V', 'I' };
  private static int[] roman_num = new int[] { 1000, 500, 100, 50,
    10, 5, 1 };

  public StringBuffer format(double number, StringBuffer toAppendTo,
    FieldPosition pos) {
    // Too lazy
    return null;
  }

  public StringBuffer format(long l, StringBuffer toAppendTo,
    FieldPosition pos) {
    StringBuffer buf = new StringBuffer(20);

    while (l > 0) {
      for (int i = 0; i < roman_num.length; i++) {
        if (l >= roman_num[i]) {
          buf.append(roman_alpha[i]);
          l -= roman_num[i];
          break;
        } else {
          int peek = i + 2 - (i % 2);
          if (peek < roman_num.length) {
            int j = roman_num[i] - roman_num[peek];
            if (l >= j && j > 0) {
              buf.append(roman_alpha[peek]);
              buf.append(roman_alpha[i]);
              l -= j;
              break;
            }
          }
        }
      }
    }

    return buf;
  }

  public Number parse(String source, ParsePosition parsePosition) {
    long l = 0;
    int max = 0;

    source = source.toUpperCase();

    for (int i = source.length() - 1; i >= 0; i--) {
      char c = source.charAt(i);
      boolean validChar = false;
      for (int j = 0; j < roman_alpha.length; j++) {
        if (roman_alpha[j] == c) {
          int k = roman_num[j];
          if (k >= max) {
            l += k;
            max = k;
          } else {
            l -= k;
          }
          validChar = true;
          break;
        }
      }
      if (!validChar) {
        parsePosition.setErrorIndex(i);
        return null;
      }
    }

    parsePosition.setIndex(source.length());
    return new Long(l);
  }
}

I was obviously pretty concerned about getting some large roman numerals through here – using a long rather than an int. Built to last.

Leave a Reply