X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..340931cb2e044a2141d11567dd0f782524e32994:/icuSources/i18n/nfrs.cpp?ds=sidebyside diff --git a/icuSources/i18n/nfrs.cpp b/icuSources/i18n/nfrs.cpp index de42d886..e26f08be 100644 --- a/icuSources/i18n/nfrs.cpp +++ b/icuSources/i18n/nfrs.cpp @@ -1,10 +1,12 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ****************************************************************************** -* Copyright (C) 1997-2012, International Business Machines +* Copyright (C) 1997-2015, International Business Machines * Corporation and others. All Rights Reserved. ****************************************************************************** * file name: nfrs.cpp -* encoding: US-ASCII +* encoding: UTF-8 * tab size: 8 (not used) * indentation:4 * @@ -21,11 +23,28 @@ #include "nfrule.h" #include "nfrlist.h" #include "patternprops.h" +#include "putilimp.h" #ifdef RBNF_DEBUG #include "cmemory.h" #endif +enum { + /** -x */ + NEGATIVE_RULE_INDEX = 0, + /** x.x */ + IMPROPER_FRACTION_RULE_INDEX = 1, + /** 0.x */ + PROPER_FRACTION_RULE_INDEX = 2, + /** x.0 */ + MASTER_RULE_INDEX = 3, + /** Inf */ + INFINITY_RULE_INDEX = 4, + /** NaN */ + NAN_RULE_INDEX = 5, + NON_NUMERICAL_RULE_LENGTH = 6 +}; + U_NAMESPACE_BEGIN #if 0 @@ -104,10 +123,6 @@ static const UChar gColon = 0x003a; static const UChar gSemicolon = 0x003b; static const UChar gLineFeed = 0x000a; -static const UChar gFourSpaces[] = -{ - 0x20, 0x20, 0x20, 0x20, 0 -}; /* " " */ static const UChar gPercentPercent[] = { 0x25, 0x25, 0 @@ -118,17 +133,17 @@ static const UChar gNoparse[] = 0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0 }; /* "@noparse" */ -NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& status) +NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions, int32_t index, UErrorCode& status) : name() , rules(0) - , negativeNumberRule(NULL) + , owner(_owner) + , fractionRules() , fIsFractionRuleSet(FALSE) , fIsPublic(FALSE) , fIsParseable(TRUE) - , fRecursionCount(0) { - for (int i = 0; i < 3; ++i) { - fractionRules[i] = NULL; + for (int32_t i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { + nonNumericalRules[i] = NULL; } if (U_FAILURE(status)) { @@ -179,7 +194,7 @@ NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& sta } void -NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* owner, UErrorCode& status) +NFRuleSet::parseRules(UnicodeString& description, UErrorCode& status) { // start by creating a Vector whose elements are Strings containing // the descriptions of the rules (one rule per element). The rules @@ -217,85 +232,103 @@ NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* o // (this isn't a for loop because we might be deleting items from // the vector-- we want to make sure we only increment i when // we _didn't_ delete aything from the vector) - uint32_t i = 0; - while (i < rules.size()) { + int32_t rulesSize = rules.size(); + for (int32_t i = 0; i < rulesSize; i++) { NFRule* rule = rules[i]; + int64_t baseValue = rule->getBaseValue(); - switch (rule->getType()) { + if (baseValue == 0) { // if the rule's base value is 0, fill in a default // base value (this will be 1 plus the preceding // rule's base value for regular rule sets, and the // same as the preceding rule's base value in fraction // rule sets) - case NFRule::kNoBase: rule->setBaseValue(defaultBaseValue, status); - if (!isFractionRuleSet()) { - ++defaultBaseValue; - } - ++i; - break; - - // if it's the negative-number rule, copy it into its own - // data member and delete it from the list - case NFRule::kNegativeNumberRule: - if (negativeNumberRule) { - delete negativeNumberRule; - } - negativeNumberRule = rules.remove(i); - break; - - // if it's the improper fraction rule, copy it into the - // correct element of fractionRules - case NFRule::kImproperFractionRule: - if (fractionRules[0]) { - delete fractionRules[0]; - } - fractionRules[0] = rules.remove(i); - break; - - // if it's the proper fraction rule, copy it into the - // correct element of fractionRules - case NFRule::kProperFractionRule: - if (fractionRules[1]) { - delete fractionRules[1]; - } - fractionRules[1] = rules.remove(i); - break; - - // if it's the master rule, copy it into the - // correct element of fractionRules - case NFRule::kMasterRule: - if (fractionRules[2]) { - delete fractionRules[2]; - } - fractionRules[2] = rules.remove(i); - break; - + } + else { // if it's a regular rule that already knows its base value, // check to make sure the rules are in order, and update // the default base value for the next rule - default: - if (rule->getBaseValue() < defaultBaseValue) { + if (baseValue < defaultBaseValue) { // throw new IllegalArgumentException("Rules are not in order"); status = U_PARSE_ERROR; return; } - defaultBaseValue = rule->getBaseValue(); - if (!isFractionRuleSet()) { - ++defaultBaseValue; - } - ++i; - break; + defaultBaseValue = baseValue; + } + if (!fIsFractionRuleSet) { + ++defaultBaseValue; } } } +/** + * Set one of the non-numerical rules. + * @param rule The rule to set. + */ +void NFRuleSet::setNonNumericalRule(NFRule *rule) { + int64_t baseValue = rule->getBaseValue(); + if (baseValue == NFRule::kNegativeNumberRule) { + delete nonNumericalRules[NEGATIVE_RULE_INDEX]; + nonNumericalRules[NEGATIVE_RULE_INDEX] = rule; + } + else if (baseValue == NFRule::kImproperFractionRule) { + setBestFractionRule(IMPROPER_FRACTION_RULE_INDEX, rule, TRUE); + } + else if (baseValue == NFRule::kProperFractionRule) { + setBestFractionRule(PROPER_FRACTION_RULE_INDEX, rule, TRUE); + } + else if (baseValue == NFRule::kMasterRule) { + setBestFractionRule(MASTER_RULE_INDEX, rule, TRUE); + } + else if (baseValue == NFRule::kInfinityRule) { + delete nonNumericalRules[INFINITY_RULE_INDEX]; + nonNumericalRules[INFINITY_RULE_INDEX] = rule; + } + else if (baseValue == NFRule::kNaNRule) { + delete nonNumericalRules[NAN_RULE_INDEX]; + nonNumericalRules[NAN_RULE_INDEX] = rule; + } +} + +/** + * Determine the best fraction rule to use. Rules matching the decimal point from + * DecimalFormatSymbols become the main set of rules to use. + * @param originalIndex The index into nonNumericalRules + * @param newRule The new rule to consider + * @param rememberRule Should the new rule be added to fractionRules. + */ +void NFRuleSet::setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule) { + if (rememberRule) { + fractionRules.add(newRule); + } + NFRule *bestResult = nonNumericalRules[originalIndex]; + if (bestResult == NULL) { + nonNumericalRules[originalIndex] = newRule; + } + else { + // We have more than one. Which one is better? + const DecimalFormatSymbols *decimalFormatSymbols = owner->getDecimalFormatSymbols(); + if (decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol).charAt(0) + == newRule->getDecimalPoint()) + { + nonNumericalRules[originalIndex] = newRule; + } + // else leave it alone + } +} + NFRuleSet::~NFRuleSet() { - delete negativeNumberRule; - delete fractionRules[0]; - delete fractionRules[1]; - delete fractionRules[2]; + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (i != IMPROPER_FRACTION_RULE_INDEX + && i != PROPER_FRACTION_RULE_INDEX + && i != MASTER_RULE_INDEX) + { + delete nonNumericalRules[i]; + } + // else it will be deleted via NFRuleList fractionRules + } } static UBool @@ -316,12 +349,16 @@ NFRuleSet::operator==(const NFRuleSet& rhs) const { if (rules.size() == rhs.rules.size() && fIsFractionRuleSet == rhs.fIsFractionRuleSet && - name == rhs.name && - util_equalRules(negativeNumberRule, rhs.negativeNumberRule) && - util_equalRules(fractionRules[0], rhs.fractionRules[0]) && - util_equalRules(fractionRules[1], rhs.fractionRules[1]) && - util_equalRules(fractionRules[2], rhs.fractionRules[2])) { + name == rhs.name) { + // ...then compare the non-numerical rule lists... + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (!util_equalRules(nonNumericalRules[i], rhs.nonNumericalRules[i])) { + return FALSE; + } + } + + // ...then compare the rule lists... for (uint32_t i = 0; i < rules.size(); ++i) { if (*rules[i] != *rhs.rules[i]) { return FALSE; @@ -332,41 +369,62 @@ NFRuleSet::operator==(const NFRuleSet& rhs) const return FALSE; } -#define RECURSION_LIMIT 50 +void +NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status) { + for (uint32_t i = 0; i < rules.size(); ++i) { + rules[i]->setDecimalFormatSymbols(newSymbols, status); + } + // Switch the fraction rules to mirror the DecimalFormatSymbols. + for (int32_t nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= MASTER_RULE_INDEX; nonNumericalIdx++) { + if (nonNumericalRules[nonNumericalIdx]) { + for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { + NFRule *fractionRule = fractionRules[fIdx]; + if (nonNumericalRules[nonNumericalIdx]->getBaseValue() == fractionRule->getBaseValue()) { + setBestFractionRule(nonNumericalIdx, fractionRule, FALSE); + } + } + } + } + + for (uint32_t nnrIdx = 0; nnrIdx < NON_NUMERICAL_RULE_LENGTH; nnrIdx++) { + NFRule *rule = nonNumericalRules[nnrIdx]; + if (rule) { + rule->setDecimalFormatSymbols(newSymbols, status); + } + } +} + +#define RECURSION_LIMIT 64 void -NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos) const +NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const { - NFRule *rule = findNormalRule(number); + if (recursionCount >= RECURSION_LIMIT) { + // stop recursion + status = U_INVALID_STATE_ERROR; + return; + } + const NFRule *rule = findNormalRule(number); if (rule) { // else error, but can't report it - NFRuleSet* ncThis = (NFRuleSet*)this; - if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) { - // stop recursion - ncThis->fRecursionCount = 0; - } else { - rule->doFormat(number, toAppendTo, pos); - ncThis->fRecursionCount--; - } + rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); } } void -NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos) const +NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const { - NFRule *rule = findDoubleRule(number); + if (recursionCount >= RECURSION_LIMIT) { + // stop recursion + status = U_INVALID_STATE_ERROR; + return; + } + const NFRule *rule = findDoubleRule(number); if (rule) { // else error, but can't report it - NFRuleSet* ncThis = (NFRuleSet*)this; - if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) { - // stop recursion - ncThis->fRecursionCount = 0; - } else { - rule->doFormat(number, toAppendTo, pos); - ncThis->fRecursionCount--; - } + rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); } } -NFRule* +const NFRule* NFRuleSet::findDoubleRule(double number) const { // if this is a fraction rule set, use findFractionRuleSetRule() @@ -374,41 +432,49 @@ NFRuleSet::findDoubleRule(double number) const return findFractionRuleSetRule(number); } + if (uprv_isNaN(number)) { + const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX]; + if (!rule) { + rule = owner->getDefaultNaNRule(); + } + return rule; + } + // if the number is negative, return the negative number rule // (if there isn't a negative-number rule, we pretend it's a // positive number) if (number < 0) { - if (negativeNumberRule) { - return negativeNumberRule; + if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { + return nonNumericalRules[NEGATIVE_RULE_INDEX]; } else { number = -number; } } + if (uprv_isInfinite(number)) { + const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX]; + if (!rule) { + rule = owner->getDefaultInfinityRule(); + } + return rule; + } + // if the number isn't an integer, we use one of the fraction rules... if (number != uprv_floor(number)) { // if the number is between 0 and 1, return the proper // fraction rule - if (number < 1 && fractionRules[1]) { - return fractionRules[1]; + if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) { + return nonNumericalRules[PROPER_FRACTION_RULE_INDEX]; } // otherwise, return the improper fraction rule - else if (fractionRules[0]) { - return fractionRules[0]; + else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) { + return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]; } } // if there's a master rule, use it to format the number - if (fractionRules[2]) { - return fractionRules[2]; - } - - // always use the last rule for infinity. It is likely that rule - // has a DecimalFormat that will do the right thing with infinity even - // if the rule's base value is strange, i.e. something larger than what - // util64_fromDouble produces below. - if (uprv_isInfinite(number) && (rules.size() > 0)) { - return rules[rules.size() - 1]; + if (nonNumericalRules[MASTER_RULE_INDEX]) { + return nonNumericalRules[MASTER_RULE_INDEX]; } // and if we haven't yet returned a rule, use findNormalRule() @@ -417,7 +483,7 @@ NFRuleSet::findDoubleRule(double number) const return findNormalRule(r); } -NFRule * +const NFRule * NFRuleSet::findNormalRule(int64_t number) const { // if this is a fraction rule set, use findFractionRuleSetRule() @@ -430,8 +496,8 @@ NFRuleSet::findNormalRule(int64_t number) const // if the number is negative, return the negative-number rule // (if there isn't one, pretend the number is positive) if (number < 0) { - if (negativeNumberRule) { - return negativeNumberRule; + if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { + return nonNumericalRules[NEGATIVE_RULE_INDEX]; } else { number = -number; } @@ -479,7 +545,7 @@ NFRuleSet::findNormalRule(int64_t number) const // an explanation of the rollback rule). If we do, roll back // one rule and return that one instead of the one we'd normally // return - if (result->shouldRollBack((double)number)) { + if (result->shouldRollBack(number)) { if (hi == 1) { // bad rule set, no prior rule to rollback to from this base return NULL; } @@ -488,7 +554,7 @@ NFRuleSet::findNormalRule(int64_t number) const return result; } // else use the master rule - return fractionRules[2]; + return nonNumericalRules[MASTER_RULE_INDEX]; } /** @@ -506,7 +572,7 @@ NFRuleSet::findNormalRule(int64_t number) const * a number between 0 and 1) * @return The rule to use to format this number */ -NFRule* +const NFRule* NFRuleSet::findFractionRuleSetRule(double number) const { // the obvious way to do this (multiply the value being formatted @@ -615,7 +681,7 @@ static void dumpUS(FILE* f, const UnicodeString& us) { #endif UBool -NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result, UBool lenient) const +NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result, UBool lenient) const { // try matching each rule in the rule set against the text being // parsed. Whichever one matches the most characters is the one @@ -639,40 +705,19 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun fprintf(stderr, "'\n"); fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0); #endif + // Try each of the negative rules, fraction rules, infinity rules and NaN rules + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (nonNumericalRules[i] && ((nonNumericalExecutedRuleMask >> i) & 1) == 0) { + // Mark this rule as being executed so that we don't try to execute it again. + nonNumericalExecutedRuleMask |= 1 << i; - // start by trying the negative number rule (if there is one) - if (negativeNumberRule) { - Formattable tempResult; -#ifdef RBNF_DEBUG - fprintf(stderr, " %x ub: %g\n", negativeNumberRule, upperBound); -#endif - UBool success = negativeNumberRule->doParse(text, workingPos, 0, upperBound, tempResult); -#ifdef RBNF_DEBUG - fprintf(stderr, " success: %d wpi: %d\n", success, workingPos.getIndex()); -#endif - if (success && workingPos.getIndex() > highWaterMark.getIndex()) { - result = tempResult; - highWaterMark = workingPos; - } - workingPos = pos; - } -#ifdef RBNF_DEBUG - fprintf(stderr, " continue fractional with text '"); - dumpUS(stderr, text); - fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); -#endif - // then try each of the fraction rules - { - for (int i = 0; i < 3; i++) { - if (fractionRules[i]) { - Formattable tempResult; - UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult, lenient || isDecimalFormatRuleParseable() ); - if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { - result = tempResult; - highWaterMark = workingPos; - } - workingPos = pos; + Formattable tempResult; + UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, tempResult, lenient || isDecimalFormatRuleParseable() ); + if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { + result = tempResult; + highWaterMark = workingPos; } + workingPos = pos; } } #ifdef RBNF_DEBUG @@ -706,7 +751,7 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun continue; } Formattable tempResult; - UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, tempResult); + UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, tempResult); if (success && workingPos.getIndex() > highWaterMark.getIndex()) { result = tempResult; highWaterMark = workingPos; @@ -728,30 +773,37 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun void NFRuleSet::appendRules(UnicodeString& result) const { + uint32_t i; + // the rule set name goes first... result.append(name); result.append(gColon); result.append(gLineFeed); // followed by the regular rules... - for (uint32_t i = 0; i < rules.size(); i++) { - result.append(gFourSpaces, 4); + for (i = 0; i < rules.size(); i++) { rules[i]->_appendRuleText(result); result.append(gLineFeed); } // followed by the special rules (if they exist) - if (negativeNumberRule) { - result.append(gFourSpaces, 4); - negativeNumberRule->_appendRuleText(result); - result.append(gLineFeed); - } - - { - for (uint32_t i = 0; i < 3; ++i) { - if (fractionRules[i]) { - result.append(gFourSpaces, 4); - fractionRules[i]->_appendRuleText(result); + for (i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { + NFRule *rule = nonNumericalRules[i]; + if (nonNumericalRules[i]) { + if (rule->getBaseValue() == NFRule::kImproperFractionRule + || rule->getBaseValue() == NFRule::kProperFractionRule + || rule->getBaseValue() == NFRule::kMasterRule) + { + for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { + NFRule *fractionRule = fractionRules[fIdx]; + if (fractionRule->getBaseValue() == rule->getBaseValue()) { + fractionRule->_appendRuleText(result); + result.append(gLineFeed); + } + } + } + else { + rule->_appendRuleText(result); result.append(gLineFeed); } } @@ -781,18 +833,23 @@ int64_t util64_fromDouble(double d) { return result; } -int64_t util64_pow(int32_t r, uint32_t e) { - if (r == 0) { +uint64_t util64_pow(uint32_t base, uint16_t exponent) { + if (base == 0) { return 0; - } else if (e == 0) { - return 1; - } else { - int64_t n = r; - while (--e > 0) { - n *= r; + } + uint64_t result = 1; + uint64_t pow = base; + while (true) { + if ((exponent & 1) == 1) { + result *= pow; + } + exponent >>= 1; + if (exponent == 0) { + break; } - return n; + pow *= pow; } + return result; } static const uint8_t asciiDigits[] = { @@ -976,4 +1033,3 @@ U_NAMESPACE_END /* U_HAVE_RBNF */ #endif -