isValidRomanNumeralValue method
Confirms or disconfirms a valid Roman numeral value. This may change for the same String depending on the RomanNumeralsConfig.
Implementation
bool isValidRomanNumeralValue({RomanNumeralsConfig? config}) {
config ??= RomanNumerals.romanNumeralsConfig;
// Is it literally just a nulla token?
final nulla = config.nulla;
if (nulla != null) {
if (this == nulla) {
return true;
}
}
/*
Match expression reads as:
- ^ # From the beginnging of the line.
- if Common Roman Numerals:
- (M{0,3}) # There are 3 maximum 'M' matches.
- else if Apostrophus:
- if compact:
- ((ↈ){0,3}) # Maxium 3 millions
- ((ↂↈ)|(ↂↇ)|(ↇ)?(ↂ){0,3}) # 900k, 400k, or 0 to 3 100k following 0 or 1 500k
- ((ↀↂ)|(ↀↁ)|(ↁ)?(ↀ){0,3}) # 90k, 40k or 0 to 3 10k following 0 o 1 50k
- ((Cↀ)|(CCCC)|(D)?C{0,3}) # Then, match for 900, 400 - 4 C's allowed!
- # then 0 to 3 100/C's following 0 or one 500/D's.
- else:
- ((CCCCIↃↃↃↃ){0,3})
- ((CCCIↃↃↃCCCCIↃↃↃↃ)|(CCCIↃↃↃIↃↃↃↃ)|(IↃↃↃↃ)?(CCCIↃↃↃ){0,3})
- ((CCIↃↃCCCIↃↃↃ)|(CCIↃↃIↃↃↃ)|(IↃↃↃ)?(CCIↃↃ){0,3})
- ((CIↃCCIↃↃ)|(CIↃIↃↃ)|(ⅠↃↃ)?(CIↃ){0,3})
- ((CCIↃ)|(CCCC)|(IↃ)?C{0,3})
- # The matching follows Vinculum style, but substituing the
- # overline for specific sequences.
- # Note: 4 CCCC's used for 400!
- else if Vinculum:
- (M̅{0,3}) # There are 3 maximum 'M̅' matches for 3,000,000
- (C̅M̅|C̅D̅|D̅?C̅{0,3}) # Then, match for 900,000/C̅M̅, 400,000/C̅D̅, or 0 to 3 100,000/C̅'s following 0 or one 500,000/D̅'s.
- (X̅C̅|X̅L̅|L̅?X̅{0,3}) # Then, match for 90,000/X̅C̅, 40,000/X̅L̅, or 0 to 3 10,000/X̅'s following 0 or one 50/L̅'s.
- (MX̅|MV̅|V̅?M{0,3}) # Then, match for 9,000/MX̅, 4,000/MV̅, or 0 to 3 1,000/M's following 0 or one 5/V̅'s.
- (CM|CD|D?C{0,3}) # Then, match for 900/CM, 400/CD, or 0 to 3 100/C's following 0 or one 500/D's.
- (XC|XL|L?X{0,3}) # Then, match for 90/XC, 40/XL, or 0 to 3 10/X's following 0 or one 50/L's.
- (IX|IV|V?I{0,3}) # Then, match for 9/IX, 4/IV, or 0 to 3 1/X's following 0 or one 5/V's.
- $ # To the end of the line.
The options provide for case-insensitive matching.
*/
var expString = r'(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})';
switch (config.configType) {
case RomanNumeralsType.common:
expString = r'(M{0,3})(CM|CD|D?C{0,3})' + expString;
break;
case RomanNumeralsType.apostrophus:
final aConfig = config as ApostrophusRomanNumeralsConfig;
if (aConfig.compact) {
expString = r'((ↈ){0,3})'
r'((ↂↈ)|(ↂↇ)|(ↇ)?(ↂ){0,3})'
r'((ↀↂ)|(ↀↁ)|(ↁ)?(ↀ){0,3})'
r'((Cↀ)|(CCCC)|(D)?C{0,3})'
'$expString';
} else {
expString = r'((CCCCIↃↃↃↃ){0,3})'
r'((CCCIↃↃↃCCCCIↃↃↃↃ)|(CCCIↃↃↃIↃↃↃↃ)|(IↃↃↃↃ)?(CCCIↃↃↃ){0,3})'
r'((CCIↃↃCCCIↃↃↃ)|(CCIↃↃIↃↃↃ)|(IↃↃↃ)?(CCIↃↃ){0,3})'
r'((CIↃCCIↃↃ)|(CIↃIↃↃ)|(ⅠↃↃ)?(CIↃ){0,3})'
r'((CCIↃ)|(CCCC)|(IↃ)?C{0,3})'
'$expString';
}
break;
case RomanNumeralsType.vinculum:
// With the unicode flag on for the RegExp below, the sequences
// in this string for unicode can be matched.
// https://api.flutter.dev/flutter/dart-core/RegExp/isUnicode.html
// > In Unicode mode, the syntax of the RegExp pattern is more
// > restricted, but some pattern features, like Unicode property
// > escapes, are only available in this mode.
expString = r'((M\u{0305}){0,3})'
r'((C\u{0305}M\u{0305})|(C\u{0305}D\u{0305})|(D\u{0305})?(C\u{0305}){0,3})'
r'((X\u{0305}C\u{0305})|(X\u{0305}L\u{0305})|(L\u{0305})?(X\u{0305}){0,3})'
r'((MX\u{0305})|(MV\u{0305})|(V\u{0305})?(M){0,3})'
r'(CM|CD|D?C{0,3})'
'$expString';
break;
}
expString = r'^' + expString + r'$';
final exp = RegExp(expString, caseSensitive: false, unicode: true);
final matches = exp.allMatches(this);
return matches.length == 1;
}