-
-
Notifications
You must be signed in to change notification settings - Fork 247
/
RomanNumeralParser.java
115 lines (103 loc) · 4.33 KB
/
RomanNumeralParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package codes.biscuit.skyblockaddons.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class for working with Roman numerals
* @author DidiSkywalker
*/
public class RomanNumeralParser {
/**
* Pattern that validates a string as a correct Roman numeral
*/
private static final Pattern NUMERAL_VALIDATION_PATTERN = Pattern.compile("^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
/**
* Pattern that finds words that begin with a Roman numeral
*/
private static final Pattern NUMERAL_FINDING_PATTERN = Pattern.compile("(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})\\w+");
private enum Numeral {
I(1),
V(5),
X(10),
L(50),
C(100),
D(500),
M(1000);
private final int value;
Numeral(int value) {
this.value = value;
}
private static Numeral getFromChar(char c) {
try {
return Numeral.valueOf(Character.toString(c));
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException("Expected valid Roman numeral, received " + c);
}
}
}
/**
* Replaces all occurrences of Roman numerals in an input string with their integer values.
* For example: VI -> 6, X -> 10, etc
*
* @param input Input string to replace numerals in
* @return The input string with all numerals replaced by integers
*/
public static String replaceNumeralsWithIntegers(String input) {
StringBuilder result = new StringBuilder(input);
int offsetByReplacement = 0; // Because integers and numerals can be of different lengths, keep track of the produced offset from replacing
Matcher matcher = NUMERAL_FINDING_PATTERN.matcher(input);
while (matcher.find()) {
// The matcher finds all words that begin with a Roman numeral as groups
String group = matcher.group();
if (isNumeralValid(group)) { // check if that word is actually a valid numeral in itself (to catch things like Vampirism)
int startIndex = matcher.start();
int endIndex = matcher.end();
int parsedInteger = parseNumeral(group);
String parsedIntegerString = String.valueOf(parsedInteger);
int lengthDiff = group.length() - parsedIntegerString.length();
result.replace(startIndex + offsetByReplacement, endIndex + offsetByReplacement, parsedIntegerString);
offsetByReplacement -= lengthDiff;
}
}
return result.toString();
}
/**
* Tests whether an input string is a valid Roman numeral.
* To be valid the numerals must be either {@code I, V, X, L, C, D, M} and in upper case
* and in correct format (meaning {@code IIII} is invalid as it should be {@code IV})
*
* @param romanNumeral String to test
* @return Whether that string represents a valid Roman numeral
*/
private static boolean isNumeralValid(String romanNumeral) {
return NUMERAL_VALIDATION_PATTERN.matcher(romanNumeral).matches();
}
/**
* Parses a valid Roman numeral string to its integer value.
* Use {@link #isNumeralValid(String)} to check.
*
* @param numeralString Numeral to parse
* @return Parsed value
* @throws IllegalArgumentException If the input is malformed
*/
private static int parseNumeral(String numeralString) {
int value = 0; // parsed value
char[] charArray = numeralString.toCharArray();
for (int i = 0; i < charArray.length; i++) {
char c = charArray[i];
Numeral numeral = Numeral.getFromChar(c);
if (i + 1 < charArray.length) {
// check next numeral to correctly evaluate IV, IX and so forth
Numeral nextNumeral = Numeral.getFromChar(charArray[i + 1]);
int diff = nextNumeral.value - numeral.value;
if (diff > 0) {
// if the next numeral is of higher value, it means their difference should be added instead
value += diff;
i++; // skip next char
continue;
}
}
value += numeral.value;
}
return value;
}
}