/*
RomanFormat.java (5.1k) is a Java class for converting to/from Roman numerals.
Whilst there are a number of styles for writing Roman numerals (several of which
are implemented in the above code), there appears to be a simple algorithm that
can correctly parse almost any 'reasonable'-looking Roman number (sufficiently
simple that I'm inclined to treat it as a test for whether a number is valid or
not).
All this code is licensed under a BSD-like license.
http://www.hawaga.org.uk/ben/tech/roman.html
*/
package figtree.ui;
import java.text.*;
/**
* This NumberFormat converts long integers to and from Roman Numeral notation.
* Once an instance has been created, the format and parse methods may be used
* as defined in java.text.NumberFormat.
*
* @author Ben Clifford
* @version $Id$
*
* $HeadURL$
*
* $LastChangedBy$
* $LastChangedDate$
* $LastChangedRevision$
*/
public class RomanFormat extends NumberFormat
{
/** This method returns null. I have found no meaningful translation
of floating point numbers to Roman numerals, however the NumberFormat
method requires that it is implemented. Perhaps should just cast
the double to a long and format accordingly.
*/
public StringBuffer format(double n, StringBuffer a, FieldPosition p)
{
return format((long)n, a, p);
}
/** This nested class is used to map Roman symbols onto their numerical
values. Used in the Roman class.
*/
public static class SymTab
{
/** Roman symbol */
char symbol;
/** Numerical value */
long value;
/** Constructor to build a SymTab from supplied symbol and value
@param s Roman symbol
@param v Numerical value
*/
public SymTab(char s, long v) { this.symbol=s; this.value=v; }
};
/** This table maps individual Roman symbols onto their numerical
values.<br>
Unfortunately, JavaDoc JDK 1.1 does not create documentation
for the inner class Roman.SymTab, so the reader
cannot see the definition.
*/
public static RomanFormat.SymTab syms[]= {
new RomanFormat.SymTab('M',1000),
new RomanFormat.SymTab('D',500),
new RomanFormat.SymTab('C',100),
new RomanFormat.SymTab('L',50),
new RomanFormat.SymTab('X',10),
new RomanFormat.SymTab('V',5),
new RomanFormat.SymTab('I',1)
};
/** This method converts a Roman Numeral string to a long integer. It does
not check that the string is in the correct format - for some incorrectly
formatted numbers, i.e. iix, it will produce a number. For others,
it will throw an exception.
@param text string of Roman Numerals
@param parsePosition the place to start parsing
@return A Long object containing the parsed Roman numeral
*/
public Number parse(String text, ParsePosition parsePosition)
{
String s = text.substring(parsePosition.getIndex());
long tot=0,max=0;
char ch[]=s.toUpperCase().toCharArray();
int i,p;
for(p=ch.length-1;p>=0;p--)
{
for(i=0;i<syms.length;i++)
{
if(syms[i].symbol==ch[p])
{
if(syms[i].value>=max)
tot+= (max = syms[i].value);
else
tot-= syms[i].value;
}
}
}
// say that we parsed the whole string
parsePosition.setIndex(s.length());
return new Long(tot);
}
/** This method converts a Roman Numeral string to a long integer. It does
not check that the string is in the correct format - for some incorrectly
formatted numbers, i.e. iix, it will produce a number. For others,
it will throw an exception.
@param s string of Roman Numerals
@return The integer representation of the Numerals
*/
public static long toLong(String s)
{
long tot=0,max=0;
char ch[]=s.toUpperCase().toCharArray();
int i,p;
for(p=ch.length-1;p>=0;p--)
{
for(i=0;i<syms.length;i++)
{
if(syms[i].symbol==ch[p])
{
if(syms[i].value>=max)
tot+= (max = syms[i].value);
else
tot-= syms[i].value;
}
}
}
return tot;
};
/** This method converts the supplied long into capitalised Roman
numerals.<br>
BUG: the method does not take account of the
<code>FieldPosition p</code> parameter.
@param n The number to be converted into Roman numerals
@param s The StringBuffer into which the output is to be placed.
@return The StringBuffer s
*/
public StringBuffer format(long n, StringBuffer s, FieldPosition p)
{
int i;
while(n>0)
{
for(i=0;i<syms.length;i++)
{
if(syms[i].value<=n)
{
int shift=i+(i%2);
if( i>0 && shift<syms.length &&
(syms[i-1].value-syms[shift].value) <= n )
{
s.append( syms[shift].symbol);
s.append( syms[i-1].symbol);
n= n - syms[i-1].value + syms[shift].value;
i=-1;
}
else
{
s.append(syms[i].symbol);
n-=syms[i].value;
i=-1;
}
}
}
}
return s;
}
/** This method converts a long integer to capitalised Roman notation.
@param n The integer to convert to Roman Numerals.
@return A String object containing the Roman Numerals.
*/
public static String toRoman(long n)
{
int i;
String s;
s="";
while(n>0)
{
for(i=0;i<syms.length;i++)
{
if(syms[i].value<=n)
{
int shift=i+(i%2);
if( i>0 && shift<syms.length &&
(syms[i-1].value-syms[shift].value) <= n )
{
s= s+ syms[shift].symbol + syms[i-1].symbol;
n= n - syms[i-1].value + syms[shift].value;
i=-1;
}
else
{
s+=syms[i].symbol;
n-=syms[i].value;
i=-1;
}
}
}
}
return s;
}
}