X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b75a7d8f3b4adbae880cab104ce2c6a50eee4db2..729e4ab9bc6618bc3d8a898e575df7f4019e29ca:/icuSources/i18n/nfsubs.cpp diff --git a/icuSources/i18n/nfsubs.cpp b/icuSources/i18n/nfsubs.cpp index b75c1fb9..0ac7ec97 100644 --- a/icuSources/i18n/nfsubs.cpp +++ b/icuSources/i18n/nfsubs.cpp @@ -1,6 +1,6 @@ /* ****************************************************************************** -* Copyright (C) 1997-2003, International Business Machines +* Copyright (C) 1997-2010, International Business Machines * Corporation and others. All Rights Reserved. ****************************************************************************** * file name: nfsubs.cpp @@ -13,7 +13,11 @@ * 10/11/2001 Doug Ported from ICU4J */ +#include +#include // for 'typeid' to work + #include "nfsubs.h" +#include "digitlst.h" #if U_HAVE_RBNF @@ -38,6 +42,288 @@ static const UChar gGreaterGreaterThan[] = 0x3E, 0x3E, 0 }; /* ">>" */ +U_NAMESPACE_BEGIN + +class SameValueSubstitution : public NFSubstitution { +public: + SameValueSubstitution(int32_t pos, + const NFRuleSet* ruleset, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status); + + virtual int64_t transformNumber(int64_t number) const { return number; } + virtual double transformNumber(double number) const { return number; } + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; } + virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; } + virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class MultiplierSubstitution : public NFSubstitution { + double divisor; + int64_t ldivisor; + +public: + MultiplierSubstitution(int32_t _pos, + double _divisor, + const NFRuleSet* _ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor) + { + ldivisor = util64_fromDouble(divisor); + if (divisor == 0) { + status = U_PARSE_ERROR; + } + } + + virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { + divisor = uprv_pow(radix, exponent); + ldivisor = util64_fromDouble(divisor); + + if(divisor == 0) { + status = U_PARSE_ERROR; + } + } + + virtual UBool operator==(const NFSubstitution& rhs) const; + + virtual int64_t transformNumber(int64_t number) const { + return number / ldivisor; + } + + virtual double transformNumber(double number) const { + if (getRuleSet()) { + return uprv_floor(number / divisor); + } else { + return number/divisor; + } + } + + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { + return newRuleValue * divisor; + } + + virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } + + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class ModulusSubstitution : public NFSubstitution { + double divisor; + int64_t ldivisor; + const NFRule* ruleToUse; +public: + ModulusSubstitution(int32_t pos, + double _divisor, + const NFRule* rulePredecessor, + const NFRuleSet* ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status); + + virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { + divisor = uprv_pow(radix, exponent); + ldivisor = util64_fromDouble(divisor); + + if (divisor == 0) { + status = U_PARSE_ERROR; + } + } + + virtual UBool operator==(const NFSubstitution& rhs) const; + + virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos) const; + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; + + virtual int64_t transformNumber(int64_t number) const { return number % ldivisor; } + virtual double transformNumber(double number) const { return uprv_fmod(number, divisor); } + + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + Formattable& result) const; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { + return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue; + } + + virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } + + virtual UBool isModulusSubstitution() const { return TRUE; } + + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class IntegralPartSubstitution : public NFSubstitution { +public: + IntegralPartSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} + + virtual int64_t transformNumber(int64_t number) const { return number; } + virtual double transformNumber(double number) const { return uprv_floor(number); } + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class FractionalPartSubstitution : public NFSubstitution { + UBool byDigits; + UBool useSpaces; + enum { kMaxDecimalDigits = 8 }; +public: + FractionalPartSubstitution(int32_t pos, + const NFRuleSet* ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status); + + virtual UBool operator==(const NFSubstitution& rhs) const; + + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} + virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } + virtual double transformNumber(double number) const { return number - uprv_floor(number); } + + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + Formattable& result) const; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; } + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class AbsoluteValueSubstitution : public NFSubstitution { +public: + AbsoluteValueSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} + + virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; } + virtual double transformNumber(double number) const { return uprv_fabs(number); } + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class NumeratorSubstitution : public NFSubstitution { + double denominator; + int64_t ldenominator; + UBool withZeros; +public: + static inline UnicodeString fixdesc(const UnicodeString& desc) { + if (desc.endsWith(LTLT, 2)) { + UnicodeString result(desc, 0, desc.length()-1); + return result; + } + return desc; + } + NumeratorSubstitution(int32_t _pos, + double _denominator, + const NFRuleSet* _ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator) + { + ldenominator = util64_fromDouble(denominator); + withZeros = description.endsWith(LTLT, 2); + } + + virtual UBool operator==(const NFSubstitution& rhs) const; + + virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; } + virtual double transformNumber(double number) const { return uprv_round(number * denominator); } + + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool /*lenientParse*/, + Formattable& result) const; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; } + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' +private: + static const UChar LTLT[2]; + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + +class NullSubstitution : public NFSubstitution { +public: + NullSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} + + virtual void toString(UnicodeString& /*result*/) const {} + virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} + virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } + virtual double transformNumber(double /*number*/) const { return 0; } + virtual UBool doParse(const UnicodeString& /*text*/, + ParsePosition& /*parsePosition*/, + double baseValue, + double /*upperBound*/, + UBool /*lenientParse*/, + Formattable& result) const + { result.setDouble(baseValue); return TRUE; } + virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called + virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called + virtual UBool isNullSubstitution() const { return TRUE; } + virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never called + +public: + static UClassID getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const; +}; + NFSubstitution* NFSubstitution::makeSubstitution(int32_t pos, const NFRule* rule, @@ -168,22 +454,22 @@ NFSubstitution::NFSubstitution(int32_t _pos, // that pattern (then set it to use the DecimalFormatSymbols // belonging to our formatter) else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { - DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols(); - if (!sym) { - status = U_MISSING_RESOURCE_ERROR; - return; - } + DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols(); + if (!sym) { + status = U_MISSING_RESOURCE_ERROR; + return; + } this->numberFormat = new DecimalFormat(workingDescription, *sym, status); /* test for NULL */ if (this->numberFormat == 0) { status = U_MEMORY_ALLOCATION_ERROR; return; } - if (U_FAILURE(status)) { - delete (DecimalFormat*)this->numberFormat; - this->numberFormat = NULL; - return; - } + if (U_FAILURE(status)) { + delete (DecimalFormat*)this->numberFormat; + this->numberFormat = NULL; + return; + } // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols()); } // if the description is ">>>", this substitution bypasses the @@ -218,7 +504,7 @@ NFSubstitution::~NFSubstitution() * @param exponent The exponent of the divisor */ void -NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/) { +NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode& /*status*/) { // a no-op for all substitutions except multiplier and modulus substitutions } @@ -227,25 +513,20 @@ NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/) { // boilerplate //----------------------------------------------------------------------- -const char NFSubstitution::fgClassID = 0; - -UClassID -NFSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) - /** - * Compares two substitutions for equality - * @param The substitution to compare this one to - * @return true if the two substitutions are functionally equivalent - */ +/** + * Compares two substitutions for equality + * @param The substitution to compare this one to + * @return true if the two substitutions are functionally equivalent + */ UBool NFSubstitution::operator==(const NFSubstitution& rhs) const { // compare class and all of the fields all substitutions have // in common // this should be called by subclasses before their own equality tests - return getDynamicClassID() == rhs.getDynamicClassID() + return typeid(*this) == typeid(rhs) && pos == rhs.pos && (ruleSet == NULL) == (rhs.ruleSet == NULL) // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? @@ -254,12 +535,12 @@ NFSubstitution::operator==(const NFSubstitution& rhs) const : (*numberFormat == *rhs.numberFormat)); } - /** - * Returns a textual description of the substitution - * @return A textual description of the substitution. This might - * not be identical to the description it was created from, but - * it'll produce the same result. - */ +/** + * Returns a textual description of the substitution + * @return A textual description of the substitution. This might + * not be identical to the description it was created from, but + * it'll produce the same result. + */ void NFSubstitution::toString(UnicodeString& text) const { @@ -273,7 +554,7 @@ NFSubstitution::toString(UnicodeString& text) const UnicodeString temp; if (ruleSet != NULL) { ruleSet->getName(temp); - } else { + } else if (numberFormat != NULL) { numberFormat->toPattern(temp); } text.append(temp); @@ -302,7 +583,7 @@ NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int3 // on the type of substitution this is, then just call its // rule set's format() method to format the result ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos); - } else { + } else if (numberFormat != NULL) { // or perform the transformation on the number (preserving // the result's fractional part if the formatter it set // to show it), then use that formatter's format() method @@ -333,10 +614,12 @@ NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32 // perform a transformation on the number being formatted that // is dependent on the type of substitution this is double numberToFormat = transformNumber(number); + DigitList digits; + digits.set(numberToFormat); // if the result is an integer, from here on out we work in integer // space (saving time and memory and preserving accuracy) - if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { + if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL && (!digits.isInfinite())) { ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos); // if the result isn't an integer, then call either our rule set's @@ -345,7 +628,7 @@ NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32 } else { if (ruleSet != NULL) { ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos); - } else { + } else if (numberFormat != NULL) { UnicodeString temp; numberFormat->format(numberToFormat, temp); toInsertInto.insert(_pos + this->pos, temp); @@ -428,7 +711,7 @@ NFSubstitution::doParse(const UnicodeString& text, } // ...or use our DecimalFormat to parse the text - } else { + } else if (numberFormat != NULL) { numberFormat->parse(text, result, parsePosition); } @@ -437,9 +720,8 @@ NFSubstitution::doParse(const UnicodeString& text, // of its own). Derive a parse result and return it as a Long, // if possible, or a Double if (parsePosition.getIndex() != 0) { - double tempResult = (result.getType() == Formattable::kLong) ? - (double)result.getLong() : - result.getDouble(); + UErrorCode status = U_ZERO_ERROR; + double tempResult = result.getDouble(status); // composeRuleValue() produces a full parse result from // the partial parse result passed to this function from @@ -489,6 +771,22 @@ NFSubstitution::isModulusSubstitution() const { return FALSE; } + /** + * @return true if this is a decimal format-only substitution + */ +UBool +NFSubstitution::isDecimalFormatSubstitutionOnly() const { + return (ruleSet == NULL && getNumberFormat() != NULL); +} + + /** + * @return true if this substitution uses another ruleSet + */ +UBool +NFSubstitution::isRuleSetSubstitutionOnly() const { + return (getNumberFormat() == NULL && ruleSet != NULL); +} + //=================================================================== // SameValueSubstitution //=================================================================== @@ -510,24 +808,13 @@ SameValueSubstitution::SameValueSubstitution(int32_t _pos, } } -const char SameValueSubstitution::fgClassID = 0; - -UClassID -SameValueSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} - +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) //=================================================================== // MultiplierSubstitution //=================================================================== -const char MultiplierSubstitution::fgClassID = 0; - -UClassID -MultiplierSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const { @@ -562,6 +849,10 @@ ModulusSubstitution::ModulusSubstitution(int32_t _pos, // substitution: rather than keeping a backpointer to the rule, // we keep a copy of the divisor + if (ldivisor == 0) { + status = U_PARSE_ERROR; + } + if (description == gGreaterGreaterGreaterThan) { // the >>> token doesn't alter how this substituion calculates the // values it uses for formatting and parsing, but it changes @@ -572,12 +863,7 @@ ModulusSubstitution::ModulusSubstitution(int32_t _pos, } } -const char ModulusSubstitution::fgClassID = 0; - -UClassID -ModulusSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const { @@ -675,7 +961,8 @@ ModulusSubstitution::doParse(const UnicodeString& text, ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result); if (parsePosition.getIndex() != 0) { - double tempResult = result.getDouble(); + UErrorCode status = U_ZERO_ERROR; + double tempResult = result.getDouble(status); tempResult = composeRuleValue(tempResult, baseValue); result.setDouble(tempResult); } @@ -689,12 +976,7 @@ ModulusSubstitution::doParse(const UnicodeString& text, // IntegralPartSubstitution //=================================================================== -const char IntegralPartSubstitution::fgClassID = 0; - -UClassID -IntegralPartSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) //=================================================================== @@ -748,35 +1030,60 @@ FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, void FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { - // if we're not in "byDigits" mode, just use the inherited - // doSubstitution() routine - if (!byDigits) { - NFSubstitution::doSubstitution(number, toInsertInto, _pos); + // if we're not in "byDigits" mode, just use the inherited + // doSubstitution() routine + if (!byDigits) { + NFSubstitution::doSubstitution(number, toInsertInto, _pos); + + // if we're in "byDigits" mode, transform the value into an integer + // by moving the decimal point eight places to the right and + // pulling digits off the right one at a time, formatting each digit + // as an integer using this substitution's owning rule set + // (this is slower, but more accurate, than doing it from the + // other end) + } else { + // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); + // // this flag keeps us from formatting trailing zeros. It starts + // // out false because we're pulling from the right, and switches + // // to true the first time we encounter a non-zero digit + // UBool doZeros = FALSE; + // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { + // int64_t digit = numberToFormat % 10; + // if (digit != 0 || doZeros) { + // if (doZeros && useSpaces) { + // toInsertInto.insert(_pos + getPos(), gSpace); + // } + // doZeros = TRUE; + // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); + // } + // numberToFormat /= 10; + // } + + DigitList dl; + dl.set(number); + dl.roundFixedPoint(20); // round to 20 fraction digits. + dl.reduce(); // Removes any trailing zeros. + + UBool pad = FALSE; + for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { + // Loop iterates over fraction digits, starting with the LSD. + // include both real digits from the number, and zeros + // to the left of the MSD but to the right of the decimal point. + if (pad && useSpaces) { + toInsertInto.insert(_pos + getPos(), gSpace); + } else { + pad = TRUE; + } + int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; + getRuleSet()->format(digit, toInsertInto, _pos + getPos()); + } - // if we're in "byDigits" mode, transform the value into an integer - // by moving the decimal point eight places to the right and - // pulling digits off the right one at a time, formatting each digit - // as an integer using this substitution's owning rule set - // (this is slower, but more accurate, than doing it from the - // other end) - } else { - int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); - // this flag keeps us from formatting trailing zeros. It starts - // out false because we're pulling from the right, and switches - // to true the first time we encounter a non-zero digit - UBool doZeros = FALSE; - for (int32_t i = 0; i < kMaxDecimalDigits; i++) { - int64_t digit = numberToFormat % 10; - if (digit != 0 || doZeros) { - if (doZeros && useSpaces) { - toInsertInto.insert(_pos + getPos(), gSpace); - } - doZeros = TRUE; - getRuleSet()->format(digit, toInsertInto, _pos + getPos()); - } - numberToFormat /= 10; - } + if (!pad) { + // hack around lack of precision in digitlist. if we would end up with + // "foo point" make sure we add a " zero" to the end. + getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); } + } } //----------------------------------------------------------------------- @@ -822,20 +1129,23 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, ParsePosition workPos(1); double result = 0; int32_t digit; - double p10 = 0.1; +// double p10 = 0.1; + DigitList dl; NumberFormat* fmt = NULL; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); Formattable temp; getRuleSet()->parse(workText, workPos, 10, temp); - digit = temp.getType() == Formattable::kLong ? - temp.getLong() : - (int32_t)temp.getDouble(); + UErrorCode status = U_ZERO_ERROR; + digit = temp.getLong(status); +// digit = temp.getType() == Formattable::kLong ? +// temp.getLong() : +// (int32_t)temp.getDouble(); if (lenientParse && workPos.getIndex() == 0) { if (!fmt) { - UErrorCode status = U_ZERO_ERROR; + status = U_ZERO_ERROR; fmt = NumberFormat::createInstance(status); if (U_FAILURE(status)) { delete fmt; @@ -844,13 +1154,14 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, } if (fmt) { fmt->parse(workText, temp, workPos); - digit = temp.getLong(); + digit = temp.getLong(status); } } if (workPos.getIndex() != 0) { - result += digit * p10; - p10 /= 10; + dl.append((char)('0' + digit)); +// result += digit * p10; +// p10 /= 10; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); workText.removeBetween(0, workPos.getIndex()); while (workText.length() > 0 && workText.charAt(0) == gSpace) { @@ -861,6 +1172,7 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, } delete fmt; + result = dl.getCount() == 0 ? 0 : dl.getDouble(); result = composeRuleValue(result, baseValue); resVal.setDouble(result); return TRUE; @@ -874,29 +1186,131 @@ FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; } -const char FractionalPartSubstitution::fgClassID = 0; - -UClassID -FractionalPartSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) //=================================================================== // AbsoluteValueSubstitution //=================================================================== -const char AbsoluteValueSubstitution::fgClassID = 0; - -UClassID -AbsoluteValueSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) //=================================================================== // NumeratorSubstitution //=================================================================== +void +NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const { + // perform a transformation on the number being formatted that + // is dependent on the type of substitution this is + + double numberToFormat = transformNumber(number); + int64_t longNF = util64_fromDouble(numberToFormat); + + const NFRuleSet* aruleSet = getRuleSet(); + if (withZeros && aruleSet != NULL) { + // if there are leading zeros in the decimal expansion then emit them + int64_t nf =longNF; + int32_t len = toInsertInto.length(); + while ((nf *= 10) < denominator) { + toInsertInto.insert(apos + getPos(), gSpace); + aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); + } + apos += toInsertInto.length() - len; + } + + // if the result is an integer, from here on out we work in integer + // space (saving time and memory and preserving accuracy) + if (numberToFormat == longNF && aruleSet != NULL) { + aruleSet->format(longNF, toInsertInto, apos + getPos()); + + // if the result isn't an integer, then call either our rule set's + // format() method or our DecimalFormat's format() method to + // format the result + } else { + if (aruleSet != NULL) { + aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); + } else { + UErrorCode status = U_ZERO_ERROR; + UnicodeString temp; + getNumberFormat()->format(numberToFormat, temp, status); + toInsertInto.insert(apos + getPos(), temp); + } + } +} + +UBool +NumeratorSubstitution::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool /*lenientParse*/, + Formattable& result) const +{ + // 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 + + // if withZeros is true, we need to count the zeros + // and use that to adjust the parse result + UErrorCode status = U_ZERO_ERROR; + int32_t zeroCount = 0; + UnicodeString workText(text); + + if (withZeros) { + ParsePosition workPos(1); + Formattable temp; + + while (workText.length() > 0 && workPos.getIndex() != 0) { + workPos.setIndex(0); + getRuleSet()->parse(workText, workPos, 1, 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 + break; + } + + ++zeroCount; + parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); + workText.remove(0, workPos.getIndex()); + while (workText.length() > 0 && workText.charAt(0) == gSpace) { + workText.remove(0, 1); + parsePosition.setIndex(parsePosition.getIndex() + 1); + } + } + + workText = text; + workText.remove(0, (int32_t)parsePosition.getIndex()); + parsePosition.setIndex(0); + } + + // 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, result); + + if (withZeros) { + // any base value will do in this case. is there a way to + // force this to not bother trying all the base values? + + // compute the 'effective' base and prescale the value down + int64_t n = result.getLong(status); // force conversion! + int64_t d = 1; + int32_t pow = 0; + while (d <= n) { + d *= 10; + ++pow; + } + // now add the zeros + while (zeroCount > 0) { + d *= 10; + --zeroCount; + } + // d is now our true denominator + result.setDouble((double)n/(double)d); + } + + return TRUE; +} + UBool NumeratorSubstitution::operator==(const NFSubstitution& rhs) const { @@ -904,23 +1318,17 @@ NumeratorSubstitution::operator==(const NFSubstitution& rhs) const denominator == ((const NumeratorSubstitution*)&rhs)->denominator; } -const char NumeratorSubstitution::fgClassID = 0; - -UClassID -NumeratorSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) +const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; + //=================================================================== // NullSubstitution //=================================================================== -const char NullSubstitution::fgClassID = 0; +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution) -UClassID -NullSubstitution::getDynamicClassID() const { - return getStaticClassID(); -} +U_NAMESPACE_END /* U_HAVE_RBNF */ #endif