mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 15:42:14 +00:00
ICU-22889 Implemented a recursion limit in the RBNF parsing code to match the one already present in the RBNF
formatting code.
This commit is contained in:
parent
17608b6b72
commit
1b33f5e30b
14 changed files with 138 additions and 33 deletions
|
@ -689,7 +689,7 @@ static void dumpUS(FILE* f, const UnicodeString& us) {
|
|||
#endif
|
||||
|
||||
UBool
|
||||
NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const
|
||||
NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, int32_t recursionCount, Formattable& result) const
|
||||
{
|
||||
// try matching each rule in the rule set against the text being
|
||||
// parsed. Whichever one matches the most characters is the one
|
||||
|
@ -697,6 +697,12 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
|
|||
|
||||
result.setLong(0);
|
||||
|
||||
// dump out if we've reached the recursion limit
|
||||
if (recursionCount >= RECURSION_LIMIT) {
|
||||
// stop recursion
|
||||
return false;
|
||||
}
|
||||
|
||||
// dump out if there's no text to parse
|
||||
if (text.length() == 0) {
|
||||
return 0;
|
||||
|
@ -720,7 +726,7 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
|
|||
nonNumericalExecutedRuleMask |= 1 << i;
|
||||
|
||||
Formattable tempResult;
|
||||
UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, tempResult);
|
||||
UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1, tempResult);
|
||||
if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
|
||||
result = tempResult;
|
||||
highWaterMark = workingPos;
|
||||
|
@ -759,7 +765,7 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
|
|||
continue;
|
||||
}
|
||||
Formattable tempResult;
|
||||
UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, tempResult);
|
||||
UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1, tempResult);
|
||||
if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark = workingPos;
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
void format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
|
||||
void format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
|
||||
|
||||
UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const;
|
||||
UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, int32_t recursionCount, Formattable& result) const;
|
||||
|
||||
void appendRules(UnicodeString& result) const; // toString
|
||||
|
||||
|
|
|
@ -918,6 +918,7 @@ NFRule::doParse(const UnicodeString& text,
|
|||
UBool isFractionRule,
|
||||
double upperBound,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& resVal) const
|
||||
{
|
||||
// internally we operate on a copy of the string being parsed
|
||||
|
@ -1021,6 +1022,7 @@ NFRule::doParse(const UnicodeString& text,
|
|||
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
|
||||
temp, pp, sub1,
|
||||
nonNumericalExecutedRuleMask,
|
||||
recursionCount,
|
||||
upperBound);
|
||||
|
||||
// if we got a successful match (or were trying to match a
|
||||
|
@ -1042,6 +1044,7 @@ NFRule::doParse(const UnicodeString& text,
|
|||
partialResult = matchToDelimiter(workText2, 0, partialResult,
|
||||
temp, pp2, sub2,
|
||||
nonNumericalExecutedRuleMask,
|
||||
recursionCount,
|
||||
upperBound);
|
||||
|
||||
// if we got a successful match on this second
|
||||
|
@ -1179,6 +1182,7 @@ NFRule::matchToDelimiter(const UnicodeString& text,
|
|||
ParsePosition& pp,
|
||||
const NFSubstitution* sub,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
double upperBound) const
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
@ -1213,6 +1217,7 @@ NFRule::matchToDelimiter(const UnicodeString& text,
|
|||
formatter->isLenient(),
|
||||
#endif
|
||||
nonNumericalExecutedRuleMask,
|
||||
recursionCount,
|
||||
result);
|
||||
|
||||
// if the substitution could match all the text up to
|
||||
|
@ -1267,6 +1272,7 @@ NFRule::matchToDelimiter(const UnicodeString& text,
|
|||
formatter->isLenient(),
|
||||
#endif
|
||||
nonNumericalExecutedRuleMask,
|
||||
recursionCount,
|
||||
result);
|
||||
if (success && (tempPP.getIndex() != 0)) {
|
||||
// if there's a successful match (or it's a null
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
UBool isFractional,
|
||||
double upperBound,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const;
|
||||
|
||||
UBool shouldRollBack(int64_t number) const;
|
||||
|
@ -98,6 +99,7 @@ private:
|
|||
double matchToDelimiter(const UnicodeString& text, int32_t startPos, double baseValue,
|
||||
const UnicodeString& delimiter, ParsePosition& pp, const NFSubstitution* sub,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
double upperBound) const;
|
||||
void stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const;
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ public:
|
|||
double upperBound,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const override;
|
||||
|
||||
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override {
|
||||
|
@ -242,6 +243,7 @@ public:
|
|||
double upperBound,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const override;
|
||||
|
||||
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; }
|
||||
|
@ -314,6 +316,7 @@ public:
|
|||
double upperBound,
|
||||
UBool /*lenientParse*/,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const override;
|
||||
|
||||
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue / oldRuleValue; }
|
||||
|
@ -706,6 +709,7 @@ NFSubstitution::doParse(const UnicodeString& text,
|
|||
double upperBound,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const
|
||||
{
|
||||
#ifdef RBNF_DEBUG
|
||||
|
@ -726,7 +730,7 @@ NFSubstitution::doParse(const UnicodeString& text,
|
|||
// on), then also try parsing the text using a default-
|
||||
// constructed NumberFormat
|
||||
if (ruleSet != nullptr) {
|
||||
ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, result);
|
||||
ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
|
||||
if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
NumberFormat* fmt = NumberFormat::createInstance(status);
|
||||
|
@ -949,18 +953,19 @@ ModulusSubstitution::doParse(const UnicodeString& text,
|
|||
double upperBound,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const
|
||||
{
|
||||
// if this isn't a >>> substitution, we can just use the
|
||||
// inherited parse() routine to do the parsing
|
||||
if (ruleToUse == nullptr) {
|
||||
return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, result);
|
||||
return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, recursionCount, result);
|
||||
|
||||
// but if it IS a >>> substitution, we have to do it here: we
|
||||
// use the specific rule's doParse() method, and then we have to
|
||||
// do some of the other work of NFRuleSet.parse()
|
||||
} else {
|
||||
ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, result);
|
||||
ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
|
||||
|
||||
if (parsePosition.getIndex() != 0) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
@ -1136,12 +1141,13 @@ FractionalPartSubstitution::doParse(const UnicodeString& text,
|
|||
double /*upperBound*/,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& resVal) const
|
||||
{
|
||||
// if we're not in byDigits mode, we can just use the inherited
|
||||
// doParse()
|
||||
if (!byDigits) {
|
||||
return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, resVal);
|
||||
return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, recursionCount, resVal);
|
||||
|
||||
// if we ARE in byDigits mode, parse the text one digit at a time
|
||||
// using this substitution's owning rule set (we do this by setting
|
||||
|
@ -1160,7 +1166,7 @@ FractionalPartSubstitution::doParse(const UnicodeString& text,
|
|||
while (workText.length() > 0 && workPos.getIndex() != 0) {
|
||||
workPos.setIndex(0);
|
||||
Formattable temp;
|
||||
getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, temp);
|
||||
getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, recursionCount, temp);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
digit = temp.getLong(status);
|
||||
// digit = temp.getType() == Formattable::kLong ?
|
||||
|
@ -1271,6 +1277,7 @@ NumeratorSubstitution::doParse(const UnicodeString& text,
|
|||
double upperBound,
|
||||
UBool /*lenientParse*/,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const
|
||||
{
|
||||
// we don't have to do anything special to do the parsing here,
|
||||
|
@ -1289,7 +1296,7 @@ NumeratorSubstitution::doParse(const UnicodeString& text,
|
|||
|
||||
while (workText.length() > 0 && workPos.getIndex() != 0) {
|
||||
workPos.setIndex(0);
|
||||
getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, temp); // parse zero or nothing at all
|
||||
getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, recursionCount, temp); // parse zero or nothing at all
|
||||
if (workPos.getIndex() == 0) {
|
||||
// we failed, either there were no more zeros, or the number was formatted with digits
|
||||
// either way, we're done
|
||||
|
@ -1311,7 +1318,7 @@ NumeratorSubstitution::doParse(const UnicodeString& text,
|
|||
}
|
||||
|
||||
// we've parsed off the zeros, now let's parse the rest from our current position
|
||||
NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, result);
|
||||
NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, recursionCount, result);
|
||||
|
||||
if (withZeros) {
|
||||
// any base value will do in this case. is there a way to
|
||||
|
|
|
@ -192,6 +192,7 @@ public:
|
|||
double upperBound,
|
||||
UBool lenientParse,
|
||||
uint32_t nonNumericalExecutedRuleMask,
|
||||
int32_t recursionCount,
|
||||
Formattable& result) const;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1362,7 +1362,7 @@ RuleBasedNumberFormat::parse(const UnicodeString& text,
|
|||
ParsePosition working_pp(0);
|
||||
Formattable working_result;
|
||||
|
||||
rp->parse(workingText, working_pp, kMaxDouble, 0, working_result);
|
||||
rp->parse(workingText, working_pp, kMaxDouble, 0, 0, working_result);
|
||||
if (working_pp.getIndex() > high_pp.getIndex()) {
|
||||
high_pp = working_pp;
|
||||
high_result = working_result;
|
||||
|
|
|
@ -81,6 +81,7 @@ void IntlTestRBNF::runIndexedTest(int32_t index, UBool exec, const char* &name,
|
|||
TESTCASE(29, TestNumberingSystem);
|
||||
TESTCASE(30, TestDFRounding);
|
||||
TESTCASE(31, TestMemoryLeak22899);
|
||||
TESTCASE(32, TestInfiniteRecursion);
|
||||
#else
|
||||
TESTCASE(0, TestRBNFDisabled);
|
||||
#endif
|
||||
|
@ -2614,6 +2615,39 @@ IntlTestRBNF::TestNumberingSystem() {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
IntlTestRBNF::TestInfiniteRecursion() {
|
||||
UnicodeString badRules[] = {
|
||||
">>",
|
||||
"<<",
|
||||
"<<<",
|
||||
">>>",
|
||||
"%foo: x=%foo=",
|
||||
"%one: x>%two>; %two: y>%one>;"
|
||||
};
|
||||
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(badRules); i++) {
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
UParseError parseErr;
|
||||
RuleBasedNumberFormat rbnf(badRules[i], parseErr, err);
|
||||
|
||||
if (U_SUCCESS(err)) {
|
||||
UnicodeString result;
|
||||
rbnf.format(5, result);
|
||||
// we don't actually care about the result and the function doesn't return an error code;
|
||||
// we just want to make sure the function returns
|
||||
|
||||
Formattable pResult;
|
||||
rbnf.parse("foo", pResult, err);
|
||||
assertTrue("rbnf.parse() didn't return U_INVALID_FORMAT_ERROR!", err == U_INVALID_FORMAT_ERROR);
|
||||
} else {
|
||||
// eventually it'd be nice to statically analyze the rules for (at least) the most common
|
||||
// causes of infinite recursion, in which case we'd end up down here and need to check
|
||||
// the error code. But for now, we probably won't end up here and don't care if we do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* U_HAVE_RBNF */
|
||||
#else
|
||||
|
||||
|
|
|
@ -162,6 +162,7 @@ class IntlTestRBNF : public IntlTest {
|
|||
void TestMinMaxIntegerDigitsIgnored();
|
||||
void TestNumberingSystem();
|
||||
void TestMemoryLeak22899();
|
||||
void TestInfiniteRecursion();
|
||||
|
||||
protected:
|
||||
virtual void doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing);
|
||||
|
|
|
@ -1908,4 +1908,47 @@ public class RbnfTest extends CoreTestFmwk {
|
|||
rbnf.setDefaultRuleSet("%ethiopic");
|
||||
assertEquals("Wrong result with Ethiopic rule set", "፻፳፫", rbnf.format(123));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestInfiniteRecursion() {
|
||||
final String[] badRules = {
|
||||
">>",
|
||||
"<<",
|
||||
"<<<",
|
||||
">>>",
|
||||
"%foo: x=%foo=",
|
||||
"%one: x>%two>; %two: y>%one>;"
|
||||
};
|
||||
|
||||
for (String badRule : badRules) {
|
||||
try {
|
||||
RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(badRule);
|
||||
try {
|
||||
rbnf.format(5);
|
||||
// we don't actually care about the result; we just want to make sure the function returns
|
||||
} catch (IllegalStateException e) {
|
||||
// we're supposed to get an IllegalStateException; swallow it and continue
|
||||
}
|
||||
|
||||
try {
|
||||
rbnf.parse("foo");
|
||||
errln("Parse test didn't throw an exception!");
|
||||
} catch (IllegalStateException e) {
|
||||
// we're supposed to get an IllegalStateException; swallow it and continue
|
||||
} catch (ParseException e) {
|
||||
// if we don't hit the recursion limit, we'll still fail to parse the number,
|
||||
// so also swallow this exception and continue
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ">>>" generates a parse exception when you try to create the formatter (so we expect that rather
|
||||
// than re-throwing and triggering a test failure)
|
||||
// (eventually it'd be nice to statically analyze the rules for (at least) the most common
|
||||
// causes of infinite recursion, in which case we'd end up down here and need to check
|
||||
// the error code. But for now, we probably won't end up here and don't care if we do)
|
||||
if (!badRule.equals("<<<")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -913,7 +913,7 @@ final class NFRule {
|
|||
* result is an integer and Double otherwise. The result is never null.
|
||||
*/
|
||||
public Number doParse(String text, ParsePosition parsePosition, boolean isFractionRule,
|
||||
double upperBound, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
|
||||
// internally we operate on a copy of the string being parsed
|
||||
// (because we're going to change it) and use our own ParsePosition
|
||||
|
@ -986,7 +986,7 @@ final class NFRule {
|
|||
pp.setIndex(0);
|
||||
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
|
||||
ruleText.substring(sub1Pos, sub2Pos), rulePatternFormat,
|
||||
pp, sub1, upperBound, nonNumericalExecutedRuleMask).doubleValue();
|
||||
pp, sub1, upperBound, nonNumericalExecutedRuleMask, recursionCount).doubleValue();
|
||||
|
||||
// if we got a successful match (or were trying to match a
|
||||
// null substitution), pp is now pointing at the first unmatched
|
||||
|
@ -1004,7 +1004,7 @@ final class NFRule {
|
|||
// a real result
|
||||
partialResult = matchToDelimiter(workText2, 0, partialResult,
|
||||
ruleText.substring(sub2Pos), rulePatternFormat, pp2, sub2,
|
||||
upperBound, nonNumericalExecutedRuleMask).doubleValue();
|
||||
upperBound, nonNumericalExecutedRuleMask, recursionCount).doubleValue();
|
||||
|
||||
// if we got a successful match on this second
|
||||
// matchToDelimiter() call, update the high-water mark
|
||||
|
@ -1132,7 +1132,7 @@ final class NFRule {
|
|||
*/
|
||||
private Number matchToDelimiter(String text, int startPos, double baseVal,
|
||||
String delimiter, PluralFormat pluralFormatDelimiter, ParsePosition pp, NFSubstitution sub,
|
||||
double upperBound, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
// if "delimiter" contains real (i.e., non-ignorable) text, search
|
||||
// it for "delimiter" beginning at "start". If that succeeds, then
|
||||
// use "sub"'s doParse() method to match the text before the
|
||||
|
@ -1155,7 +1155,7 @@ final class NFRule {
|
|||
String subText = text.substring(0, dPos);
|
||||
if (subText.length() > 0) {
|
||||
tempResult = sub.doParse(subText, tempPP, baseVal, upperBound,
|
||||
formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask);
|
||||
formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask, recursionCount);
|
||||
|
||||
// if the substitution could match all the text up to
|
||||
// where we found "delimiter", then this function has
|
||||
|
@ -1203,7 +1203,7 @@ final class NFRule {
|
|||
Number result = ZERO;
|
||||
// try to match the whole string against the substitution
|
||||
Number tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
|
||||
formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask);
|
||||
formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask, recursionCount);
|
||||
if (tempPP.getIndex() != 0) {
|
||||
// if there's a successful match (or it's a null
|
||||
// substitution), update pp to point to the first
|
||||
|
|
|
@ -751,7 +751,7 @@ final class NFRuleSet {
|
|||
* this function returns Long.valueOf(0), and the parse position is
|
||||
* left unchanged.
|
||||
*/
|
||||
public Number parse(String text, ParsePosition parsePosition, double upperBound, int nonNumericalExecutedRuleMask) {
|
||||
public Number parse(String text, ParsePosition parsePosition, double upperBound, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
// try matching each rule in the rule set against the text being
|
||||
// parsed. Whichever one matches the most characters is the one
|
||||
// that determines the value we return.
|
||||
|
@ -760,6 +760,11 @@ final class NFRuleSet {
|
|||
Number result = NFRule.ZERO;
|
||||
Number tempResult;
|
||||
|
||||
// dump out if we've reached the recursion limit
|
||||
if (recursionCount >= RECURSION_LIMIT) {
|
||||
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
||||
}
|
||||
|
||||
// dump out if there's no text to parse
|
||||
if (text.length() == 0) {
|
||||
return result;
|
||||
|
@ -772,7 +777,7 @@ final class NFRuleSet {
|
|||
// Mark this rule as being executed so that we don't try to execute it again.
|
||||
nonNumericalExecutedRuleMask |= 1 << nonNumericalRuleIdx;
|
||||
|
||||
tempResult = nonNumericalRule.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask);
|
||||
tempResult = nonNumericalRule.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1);
|
||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark.setIndex(parsePosition.getIndex());
|
||||
|
@ -799,7 +804,7 @@ final class NFRuleSet {
|
|||
continue;
|
||||
}
|
||||
|
||||
tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound, nonNumericalExecutedRuleMask);
|
||||
tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1);
|
||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark.setIndex(parsePosition.getIndex());
|
||||
|
|
|
@ -420,7 +420,7 @@ abstract class NFSubstitution {
|
|||
* is left unchanged.
|
||||
*/
|
||||
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
Number tempResult;
|
||||
|
||||
// figure out the highest base value a rule can have and match
|
||||
|
@ -438,7 +438,7 @@ abstract class NFSubstitution {
|
|||
// on), then also try parsing the text using a default-
|
||||
// constructed NumberFormat
|
||||
if (ruleSet != null) {
|
||||
tempResult = ruleSet.parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask);
|
||||
tempResult = ruleSet.parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, recursionCount);
|
||||
if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {
|
||||
tempResult = ruleSet.owner.getDecimalFormat().parse(text, parsePosition);
|
||||
}
|
||||
|
@ -1012,17 +1012,17 @@ class ModulusSubstitution extends NFSubstitution {
|
|||
*/
|
||||
@Override
|
||||
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
// if this isn't a >>> substitution, we can just use the
|
||||
// inherited parse() routine to do the parsing
|
||||
if (ruleToUse == null) {
|
||||
return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask);
|
||||
return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, recursionCount);
|
||||
|
||||
} else {
|
||||
// but if it IS a >>> substitution, we have to do it here: we
|
||||
// use the specific rule's doParse() method, and then we have to
|
||||
// do some of the other work of NFRuleSet.parse()
|
||||
Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask);
|
||||
Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, recursionCount);
|
||||
|
||||
if (parsePosition.getIndex() != 0) {
|
||||
double result = tempResult.doubleValue();
|
||||
|
@ -1317,11 +1317,11 @@ class FractionalPartSubstitution extends NFSubstitution {
|
|||
*/
|
||||
@Override
|
||||
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
// if we're not in byDigits mode, we can just use the inherited
|
||||
// doParse()
|
||||
if (!byDigits) {
|
||||
return super.doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask);
|
||||
return super.doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, recursionCount);
|
||||
}
|
||||
else {
|
||||
// if we ARE in byDigits mode, parse the text one digit at a time
|
||||
|
@ -1337,7 +1337,7 @@ class FractionalPartSubstitution extends NFSubstitution {
|
|||
int totalDigits = 0;
|
||||
while (workText.length() > 0 && workPos.getIndex() != 0) {
|
||||
workPos.setIndex(0);
|
||||
digit = ruleSet.parse(workText, workPos, 10, nonNumericalExecutedRuleMask).intValue();
|
||||
digit = ruleSet.parse(workText, workPos, 10, nonNumericalExecutedRuleMask, recursionCount).intValue();
|
||||
if (lenientParse && workPos.getIndex() == 0) {
|
||||
Number n = ruleSet.owner.getDecimalFormat().parse(workText, workPos);
|
||||
if (n != null) {
|
||||
|
@ -1640,7 +1640,7 @@ class NumeratorSubstitution extends NFSubstitution {
|
|||
*/
|
||||
@Override
|
||||
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
|
||||
double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask, int recursionCount) {
|
||||
// we don't have to do anything special to do the parsing here,
|
||||
// but we have to turn lenient parsing off-- if we leave it on,
|
||||
// it SERIOUSLY messes up the algorithm
|
||||
|
@ -1655,7 +1655,7 @@ class NumeratorSubstitution extends NFSubstitution {
|
|||
|
||||
while (workText.length() > 0 && workPos.getIndex() != 0) {
|
||||
workPos.setIndex(0);
|
||||
/*digit = */ruleSet.parse(workText, workPos, 1, nonNumericalExecutedRuleMask).intValue(); // parse zero or nothing at all
|
||||
/*digit = */ruleSet.parse(workText, workPos, 1, nonNumericalExecutedRuleMask, recursionCount).intValue(); // parse zero or nothing at all
|
||||
if (workPos.getIndex() == 0) {
|
||||
// we failed, either there were no more zeros, or the number was formatted with digits
|
||||
// either way, we're done
|
||||
|
@ -1676,7 +1676,7 @@ class NumeratorSubstitution extends NFSubstitution {
|
|||
}
|
||||
|
||||
// we've parsed off the zeros, now let's parse the rest from our current position
|
||||
Number result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask);
|
||||
Number result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, recursionCount);
|
||||
|
||||
if (withZeros) {
|
||||
// any base value will do in this case. is there a way to
|
||||
|
|
|
@ -1329,7 +1329,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||
|
||||
// try parsing the string with the rule set. If it gets past the
|
||||
// high-water mark, update the high-water mark and the result
|
||||
tempResult = ruleSets[i].parse(workingText, workingPos, Double.MAX_VALUE, 0);
|
||||
tempResult = ruleSets[i].parse(workingText, workingPos, Double.MAX_VALUE, 0, 0);
|
||||
if (workingPos.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark.setIndex(workingPos.getIndex());
|
||||
|
|
Loading…
Add table
Reference in a new issue