michael@0: /* michael@0: ****************************************************************************** michael@0: * Copyright (C) 1997-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: ****************************************************************************** michael@0: * file name: nfsubs.cpp michael@0: * encoding: US-ASCII michael@0: * tab size: 8 (not used) michael@0: * indentation:4 michael@0: * michael@0: * Modification history michael@0: * Date Name Comments michael@0: * 10/11/2001 Doug Ported from ICU4J michael@0: */ michael@0: michael@0: #include michael@0: #include "utypeinfo.h" // for 'typeid' to work michael@0: michael@0: #include "nfsubs.h" michael@0: #include "digitlst.h" michael@0: michael@0: #if U_HAVE_RBNF michael@0: michael@0: static const UChar gLessThan = 0x003c; michael@0: static const UChar gEquals = 0x003d; michael@0: static const UChar gGreaterThan = 0x003e; michael@0: static const UChar gPercent = 0x0025; michael@0: static const UChar gPound = 0x0023; michael@0: static const UChar gZero = 0x0030; michael@0: static const UChar gSpace = 0x0020; michael@0: michael@0: static const UChar gEqualsEquals[] = michael@0: { michael@0: 0x3D, 0x3D, 0 michael@0: }; /* "==" */ michael@0: static const UChar gGreaterGreaterGreaterThan[] = michael@0: { michael@0: 0x3E, 0x3E, 0x3E, 0 michael@0: }; /* ">>>" */ michael@0: static const UChar gGreaterGreaterThan[] = michael@0: { michael@0: 0x3E, 0x3E, 0 michael@0: }; /* ">>" */ michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: class SameValueSubstitution : public NFSubstitution { michael@0: public: michael@0: SameValueSubstitution(int32_t pos, michael@0: const NFRuleSet* ruleset, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status); michael@0: virtual ~SameValueSubstitution(); michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { return number; } michael@0: virtual double transformNumber(double number) const { return number; } michael@0: virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; } michael@0: virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; } michael@0: virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: SameValueSubstitution::~SameValueSubstitution() {} michael@0: michael@0: class MultiplierSubstitution : public NFSubstitution { michael@0: double divisor; michael@0: int64_t ldivisor; michael@0: michael@0: public: michael@0: MultiplierSubstitution(int32_t _pos, michael@0: double _divisor, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor) michael@0: { michael@0: ldivisor = util64_fromDouble(divisor); michael@0: if (divisor == 0) { michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: } michael@0: virtual ~MultiplierSubstitution(); michael@0: michael@0: virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { michael@0: divisor = uprv_pow(radix, exponent); michael@0: ldivisor = util64_fromDouble(divisor); michael@0: michael@0: if(divisor == 0) { michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: } michael@0: michael@0: virtual UBool operator==(const NFSubstitution& rhs) const; michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { michael@0: return number / ldivisor; michael@0: } michael@0: michael@0: virtual double transformNumber(double number) const { michael@0: if (getRuleSet()) { michael@0: return uprv_floor(number / divisor); michael@0: } else { michael@0: return number/divisor; michael@0: } michael@0: } michael@0: michael@0: virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { michael@0: return newRuleValue * divisor; michael@0: } michael@0: michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } michael@0: michael@0: virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: MultiplierSubstitution::~MultiplierSubstitution() {} michael@0: michael@0: class ModulusSubstitution : public NFSubstitution { michael@0: double divisor; michael@0: int64_t ldivisor; michael@0: const NFRule* ruleToUse; michael@0: public: michael@0: ModulusSubstitution(int32_t pos, michael@0: double _divisor, michael@0: const NFRule* rulePredecessor, michael@0: const NFRuleSet* ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status); michael@0: virtual ~ModulusSubstitution(); michael@0: michael@0: virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { michael@0: divisor = uprv_pow(radix, exponent); michael@0: ldivisor = util64_fromDouble(divisor); michael@0: michael@0: if (divisor == 0) { michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: } michael@0: michael@0: virtual UBool operator==(const NFSubstitution& rhs) const; michael@0: michael@0: virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos) const; michael@0: virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { return number % ldivisor; } michael@0: virtual double transformNumber(double number) const { return uprv_fmod(number, divisor); } michael@0: michael@0: virtual UBool doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool lenientParse, michael@0: Formattable& result) const; michael@0: michael@0: virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { michael@0: return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue; michael@0: } michael@0: michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } michael@0: michael@0: virtual UBool isModulusSubstitution() const { return TRUE; } michael@0: michael@0: virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' michael@0: michael@0: virtual void toString(UnicodeString& result) const; michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: ModulusSubstitution::~ModulusSubstitution() {} michael@0: michael@0: class IntegralPartSubstitution : public NFSubstitution { michael@0: public: michael@0: IntegralPartSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} michael@0: virtual ~IntegralPartSubstitution(); michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { return number; } michael@0: virtual double transformNumber(double number) const { return uprv_floor(number); } michael@0: virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } michael@0: virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: IntegralPartSubstitution::~IntegralPartSubstitution() {} michael@0: michael@0: class FractionalPartSubstitution : public NFSubstitution { michael@0: UBool byDigits; michael@0: UBool useSpaces; michael@0: enum { kMaxDecimalDigits = 8 }; michael@0: public: michael@0: FractionalPartSubstitution(int32_t pos, michael@0: const NFRuleSet* ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status); michael@0: virtual ~FractionalPartSubstitution(); michael@0: michael@0: virtual UBool operator==(const NFSubstitution& rhs) const; michael@0: michael@0: virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; michael@0: virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} michael@0: virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } michael@0: virtual double transformNumber(double number) const { return number - uprv_floor(number); } michael@0: michael@0: virtual UBool doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool lenientParse, michael@0: Formattable& result) const; michael@0: michael@0: virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; } michael@0: virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: FractionalPartSubstitution::~FractionalPartSubstitution() {} michael@0: michael@0: class AbsoluteValueSubstitution : public NFSubstitution { michael@0: public: michael@0: AbsoluteValueSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} michael@0: virtual ~AbsoluteValueSubstitution(); michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; } michael@0: virtual double transformNumber(double number) const { return uprv_fabs(number); } michael@0: virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; } michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } michael@0: virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} michael@0: michael@0: class NumeratorSubstitution : public NFSubstitution { michael@0: double denominator; michael@0: int64_t ldenominator; michael@0: UBool withZeros; michael@0: public: michael@0: static inline UnicodeString fixdesc(const UnicodeString& desc) { michael@0: if (desc.endsWith(LTLT, 2)) { michael@0: UnicodeString result(desc, 0, desc.length()-1); michael@0: return result; michael@0: } michael@0: return desc; michael@0: } michael@0: NumeratorSubstitution(int32_t _pos, michael@0: double _denominator, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator) michael@0: { michael@0: ldenominator = util64_fromDouble(denominator); michael@0: withZeros = description.endsWith(LTLT, 2); michael@0: } michael@0: virtual ~NumeratorSubstitution(); michael@0: michael@0: virtual UBool operator==(const NFSubstitution& rhs) const; michael@0: michael@0: virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; } michael@0: virtual double transformNumber(double number) const { return uprv_round(number * denominator); } michael@0: michael@0: virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} michael@0: virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; michael@0: virtual UBool doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool /*lenientParse*/, michael@0: Formattable& result) const; michael@0: michael@0: virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; } michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; } michael@0: virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' michael@0: private: michael@0: static const UChar LTLT[2]; michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: NumeratorSubstitution::~NumeratorSubstitution() {} michael@0: michael@0: class NullSubstitution : public NFSubstitution { michael@0: public: michael@0: NullSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} michael@0: virtual ~NullSubstitution(); michael@0: michael@0: virtual void toString(UnicodeString& /*result*/) const {} michael@0: virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} michael@0: virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} michael@0: virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } michael@0: virtual double transformNumber(double /*number*/) const { return 0; } michael@0: virtual UBool doParse(const UnicodeString& /*text*/, michael@0: ParsePosition& /*parsePosition*/, michael@0: double baseValue, michael@0: double /*upperBound*/, michael@0: UBool /*lenientParse*/, michael@0: Formattable& result) const michael@0: { result.setDouble(baseValue); return TRUE; } michael@0: virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called michael@0: virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called michael@0: virtual UBool isNullSubstitution() const { return TRUE; } michael@0: virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never called michael@0: michael@0: public: michael@0: static UClassID getStaticClassID(void); michael@0: virtual UClassID getDynamicClassID(void) const; michael@0: }; michael@0: michael@0: NullSubstitution::~NullSubstitution() {} michael@0: michael@0: NFSubstitution* michael@0: NFSubstitution::makeSubstitution(int32_t pos, michael@0: const NFRule* rule, michael@0: const NFRule* predecessor, michael@0: const NFRuleSet* ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: { michael@0: // if the description is empty, return a NullSubstitution michael@0: if (description.length() == 0) { michael@0: return new NullSubstitution(pos, ruleSet, formatter, description, status); michael@0: } michael@0: michael@0: switch (description.charAt(0)) { michael@0: // if the description begins with '<'... michael@0: case gLessThan: michael@0: // throw an exception if the rule is a negative number michael@0: // rule michael@0: if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { michael@0: // throw new IllegalArgumentException("<< not allowed in negative-number rule"); michael@0: status = U_PARSE_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: // if the rule is a fraction rule, return an michael@0: // IntegralPartSubstitution michael@0: else if (rule->getBaseValue() == NFRule::kImproperFractionRule michael@0: || rule->getBaseValue() == NFRule::kProperFractionRule michael@0: || rule->getBaseValue() == NFRule::kMasterRule) { michael@0: return new IntegralPartSubstitution(pos, ruleSet, formatter, description, status); michael@0: } michael@0: michael@0: // if the rule set containing the rule is a fraction michael@0: // rule set, return a NumeratorSubstitution michael@0: else if (ruleSet->isFractionRuleSet()) { michael@0: return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), michael@0: formatter->getDefaultRuleSet(), formatter, description, status); michael@0: } michael@0: michael@0: // otherwise, return a MultiplierSubstitution michael@0: else { michael@0: return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet, michael@0: formatter, description, status); michael@0: } michael@0: michael@0: // if the description begins with '>'... michael@0: case gGreaterThan: michael@0: // if the rule is a negative-number rule, return michael@0: // an AbsoluteValueSubstitution michael@0: if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { michael@0: return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description, status); michael@0: } michael@0: michael@0: // if the rule is a fraction rule, return a michael@0: // FractionalPartSubstitution michael@0: else if (rule->getBaseValue() == NFRule::kImproperFractionRule michael@0: || rule->getBaseValue() == NFRule::kProperFractionRule michael@0: || rule->getBaseValue() == NFRule::kMasterRule) { michael@0: return new FractionalPartSubstitution(pos, ruleSet, formatter, description, status); michael@0: } michael@0: michael@0: // if the rule set owning the rule is a fraction rule set, michael@0: // throw an exception michael@0: else if (ruleSet->isFractionRuleSet()) { michael@0: // throw new IllegalArgumentException(">> not allowed in fraction rule set"); michael@0: status = U_PARSE_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: // otherwise, return a ModulusSubstitution michael@0: else { michael@0: return new ModulusSubstitution(pos, rule->getDivisor(), predecessor, michael@0: ruleSet, formatter, description, status); michael@0: } michael@0: michael@0: // if the description begins with '=', always return a michael@0: // SameValueSubstitution michael@0: case gEquals: michael@0: return new SameValueSubstitution(pos, ruleSet, formatter, description, status); michael@0: michael@0: // and if it's anything else, throw an exception michael@0: default: michael@0: // throw new IllegalArgumentException("Illegal substitution character"); michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: NFSubstitution::NFSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : pos(_pos), ruleSet(NULL), numberFormat(NULL) michael@0: { michael@0: // the description should begin and end with the same character. michael@0: // If it doesn't that's a syntax error. Otherwise, michael@0: // makeSubstitution() was the only thing that needed to know michael@0: // about these characters, so strip them off michael@0: UnicodeString workingDescription(description); michael@0: if (description.length() >= 2 michael@0: && description.charAt(0) == description.charAt(description.length() - 1)) michael@0: { michael@0: workingDescription.remove(description.length() - 1, 1); michael@0: workingDescription.remove(0, 1); michael@0: } michael@0: else if (description.length() != 0) { michael@0: // throw new IllegalArgumentException("Illegal substitution syntax"); michael@0: status = U_PARSE_ERROR; michael@0: return; michael@0: } michael@0: michael@0: // if the description was just two paired token characters michael@0: // (i.e., "<<" or ">>"), it uses the rule set it belongs to to michael@0: // format its result michael@0: if (workingDescription.length() == 0) { michael@0: this->ruleSet = _ruleSet; michael@0: } michael@0: // if the description contains a rule set name, that's the rule michael@0: // set we use to format the result: get a reference to the michael@0: // names rule set michael@0: else if (workingDescription.charAt(0) == gPercent) { michael@0: this->ruleSet = formatter->findRuleSet(workingDescription, status); michael@0: } michael@0: // if the description begins with 0 or #, treat it as a michael@0: // DecimalFormat pattern, and initialize a DecimalFormat with michael@0: // that pattern (then set it to use the DecimalFormatSymbols michael@0: // belonging to our formatter) michael@0: else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { michael@0: DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols(); michael@0: if (!sym) { michael@0: status = U_MISSING_RESOURCE_ERROR; michael@0: return; michael@0: } michael@0: this->numberFormat = new DecimalFormat(workingDescription, *sym, status); michael@0: /* test for NULL */ michael@0: if (this->numberFormat == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: delete (DecimalFormat*)this->numberFormat; michael@0: this->numberFormat = NULL; michael@0: return; michael@0: } michael@0: // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols()); michael@0: } michael@0: // if the description is ">>>", this substitution bypasses the michael@0: // usual rule-search process and always uses the rule that precedes michael@0: // it in its own rule set's rule list (this is used for place-value michael@0: // notations: formats where you want to see a particular part of michael@0: // a number even when it's 0) michael@0: else if (workingDescription.charAt(0) == gGreaterThan) { michael@0: // this causes problems when >>> is used in a frationalPartSubstitution michael@0: // this->ruleSet = NULL; michael@0: this->ruleSet = _ruleSet; michael@0: this->numberFormat = NULL; michael@0: } michael@0: // and of the description is none of these things, it's a syntax error michael@0: else { michael@0: // throw new IllegalArgumentException("Illegal substitution syntax"); michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: } michael@0: michael@0: NFSubstitution::~NFSubstitution() michael@0: { michael@0: // cast away const michael@0: delete (NumberFormat*)numberFormat; numberFormat = NULL; michael@0: } michael@0: michael@0: /** michael@0: * Set's the substitution's divisor. Used by NFRule.setBaseValue(). michael@0: * A no-op for all substitutions except multiplier and modulus michael@0: * substitutions. michael@0: * @param radix The radix of the divisor michael@0: * @param exponent The exponent of the divisor michael@0: */ michael@0: void michael@0: NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode& /*status*/) { michael@0: // a no-op for all substitutions except multiplier and modulus substitutions michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // boilerplate michael@0: //----------------------------------------------------------------------- michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) michael@0: michael@0: /** michael@0: * Compares two substitutions for equality michael@0: * @param The substitution to compare this one to michael@0: * @return true if the two substitutions are functionally equivalent michael@0: */ michael@0: UBool michael@0: NFSubstitution::operator==(const NFSubstitution& rhs) const michael@0: { michael@0: // compare class and all of the fields all substitutions have michael@0: // in common michael@0: // this should be called by subclasses before their own equality tests michael@0: return typeid(*this) == typeid(rhs) michael@0: && pos == rhs.pos michael@0: && (ruleSet == NULL) == (rhs.ruleSet == NULL) michael@0: // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? michael@0: && (numberFormat == NULL michael@0: ? (rhs.numberFormat == NULL) michael@0: : (*numberFormat == *rhs.numberFormat)); michael@0: } michael@0: michael@0: /** michael@0: * Returns a textual description of the substitution michael@0: * @return A textual description of the substitution. This might michael@0: * not be identical to the description it was created from, but michael@0: * it'll produce the same result. michael@0: */ michael@0: void michael@0: NFSubstitution::toString(UnicodeString& text) const michael@0: { michael@0: // use tokenChar() to get the character at the beginning and michael@0: // end of the substitutin token. In between them will go michael@0: // either the name of the rule set it uses, or the pattern of michael@0: // the DecimalFormat it uses michael@0: text.remove(); michael@0: text.append(tokenChar()); michael@0: michael@0: UnicodeString temp; michael@0: if (ruleSet != NULL) { michael@0: ruleSet->getName(temp); michael@0: } else if (numberFormat != NULL) { michael@0: numberFormat->toPattern(temp); michael@0: } michael@0: text.append(temp); michael@0: text.append(tokenChar()); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // formatting michael@0: //----------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Performs a mathematical operation on the number, formats it using michael@0: * either ruleSet or decimalFormat, and inserts the result into michael@0: * toInsertInto. michael@0: * @param number The number being formatted. michael@0: * @param toInsertInto The string we insert the result into michael@0: * @param pos The position in toInsertInto where the owning rule's michael@0: * rule text begins (this value is added to this substitution's michael@0: * position to determine exactly where to insert the new text) michael@0: */ michael@0: void michael@0: NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const michael@0: { michael@0: if (ruleSet != NULL) { michael@0: // perform a transformation on the number that is dependent michael@0: // on the type of substitution this is, then just call its michael@0: // rule set's format() method to format the result michael@0: ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos); michael@0: } else if (numberFormat != NULL) { michael@0: // or perform the transformation on the number (preserving michael@0: // the result's fractional part if the formatter it set michael@0: // to show it), then use that formatter's format() method michael@0: // to format the result michael@0: double numberToFormat = transformNumber((double)number); michael@0: if (numberFormat->getMaximumFractionDigits() == 0) { michael@0: numberToFormat = uprv_floor(numberToFormat); michael@0: } michael@0: michael@0: UnicodeString temp; michael@0: numberFormat->format(numberToFormat, temp); michael@0: toInsertInto.insert(_pos + this->pos, temp); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Performs a mathematical operation on the number, formats it using michael@0: * either ruleSet or decimalFormat, and inserts the result into michael@0: * toInsertInto. michael@0: * @param number The number being formatted. michael@0: * @param toInsertInto The string we insert the result into michael@0: * @param pos The position in toInsertInto where the owning rule's michael@0: * rule text begins (this value is added to this substitution's michael@0: * position to determine exactly where to insert the new text) michael@0: */ michael@0: void michael@0: NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { michael@0: // perform a transformation on the number being formatted that michael@0: // is dependent on the type of substitution this is michael@0: double numberToFormat = transformNumber(number); michael@0: michael@0: // if the result is an integer, from here on out we work in integer michael@0: // space (saving time and memory and preserving accuracy) michael@0: if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { michael@0: ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos); michael@0: michael@0: // if the result isn't an integer, then call either our rule set's michael@0: // format() method or our DecimalFormat's format() method to michael@0: // format the result michael@0: } else { michael@0: if (ruleSet != NULL) { michael@0: ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos); michael@0: } else if (numberFormat != NULL) { michael@0: UnicodeString temp; michael@0: numberFormat->format(numberToFormat, temp); michael@0: toInsertInto.insert(_pos + this->pos, temp); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // parsing michael@0: //----------------------------------------------------------------------- michael@0: michael@0: #ifdef RBNF_DEBUG michael@0: #include michael@0: #endif michael@0: michael@0: /** michael@0: * Parses a string using the rule set or DecimalFormat belonging michael@0: * to this substitution. If there's a match, a mathematical michael@0: * operation (the inverse of the one used in formatting) is michael@0: * performed on the result of the parse and the value passed in michael@0: * and returned as the result. The parse position is updated to michael@0: * point to the first unmatched character in the string. michael@0: * @param text The string to parse michael@0: * @param parsePosition On entry, ignored, but assumed to be 0. michael@0: * On exit, this is updated to point to the first unmatched michael@0: * character (or 0 if the substitution didn't match) michael@0: * @param baseValue A partial parse result that should be michael@0: * combined with the result of this parse michael@0: * @param upperBound When searching the rule set for a rule michael@0: * matching the string passed in, only rules with base values michael@0: * lower than this are considered michael@0: * @param lenientParse If true and matching against rules fails, michael@0: * the substitution will also try matching the text against michael@0: * numerals using a default-costructed NumberFormat. If false, michael@0: * no extra work is done. (This value is false whenever the michael@0: * formatter isn't in lenient-parse mode, but is also false michael@0: * under some conditions even when the formatter _is_ in michael@0: * lenient-parse mode.) michael@0: * @return If there's a match, this is the result of composing michael@0: * baseValue with whatever was returned from matching the michael@0: * characters. This will be either a Long or a Double. If there's michael@0: * no match this is new Long(0) (not null), and parsePosition michael@0: * is left unchanged. michael@0: */ michael@0: UBool michael@0: NFSubstitution::doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool lenientParse, michael@0: Formattable& result) const michael@0: { michael@0: #ifdef RBNF_DEBUG michael@0: fprintf(stderr, " %x bv: %g ub: %g\n", this, baseValue, upperBound); michael@0: #endif michael@0: // figure out the highest base value a rule can have and match michael@0: // the text being parsed (this varies according to the type of michael@0: // substitutions: multiplier, modulus, and numerator substitutions michael@0: // restrict the search to rules with base values lower than their michael@0: // own; same-value substitutions leave the upper bound wherever michael@0: // it was, and the others allow any rule to match michael@0: upperBound = calcUpperBound(upperBound); michael@0: michael@0: // use our rule set to parse the text. If that fails and michael@0: // lenient parsing is enabled (this is always false if the michael@0: // formatter's lenient-parsing mode is off, but it may also michael@0: // be false even when the formatter's lenient-parse mode is michael@0: // on), then also try parsing the text using a default- michael@0: // constructed NumberFormat michael@0: if (ruleSet != NULL) { michael@0: ruleSet->parse(text, parsePosition, upperBound, result); michael@0: if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: NumberFormat* fmt = NumberFormat::createInstance(status); michael@0: if (U_SUCCESS(status)) { michael@0: fmt->parse(text, result, parsePosition); michael@0: } michael@0: delete fmt; michael@0: } michael@0: michael@0: // ...or use our DecimalFormat to parse the text michael@0: } else if (numberFormat != NULL) { michael@0: numberFormat->parse(text, result, parsePosition); michael@0: } michael@0: michael@0: // if the parse was successful, we've already advanced the caller's michael@0: // parse position (this is the one function that doesn't have one michael@0: // of its own). Derive a parse result and return it as a Long, michael@0: // if possible, or a Double michael@0: if (parsePosition.getIndex() != 0) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: double tempResult = result.getDouble(status); michael@0: michael@0: // composeRuleValue() produces a full parse result from michael@0: // the partial parse result passed to this function from michael@0: // the caller (this is either the owning rule's base value michael@0: // or the partial result obtained from composing the michael@0: // owning rule's base value with its other substitution's michael@0: // parse result) and the partial parse result obtained by michael@0: // matching the substitution (which will be the same value michael@0: // the caller would get by parsing just this part of the michael@0: // text with RuleBasedNumberFormat.parse() ). How the two michael@0: // values are used to derive the full parse result depends michael@0: // on the types of substitutions: For a regular rule, the michael@0: // ultimate result is its multiplier substitution's result michael@0: // times the rule's divisor (or the rule's base value) plus michael@0: // the modulus substitution's result (which will actually michael@0: // supersede part of the rule's base value). For a negative- michael@0: // number rule, the result is the negative of its substitution's michael@0: // result. For a fraction rule, it's the sum of its two michael@0: // substitution results. For a rule in a fraction rule set, michael@0: // it's the numerator substitution's result divided by michael@0: // the rule's base value. Results from same-value substitutions michael@0: // propagate back upard, and null substitutions don't affect michael@0: // the result. michael@0: tempResult = composeRuleValue(tempResult, baseValue); michael@0: result.setDouble(tempResult); michael@0: return TRUE; michael@0: // if the parse was UNsuccessful, return 0 michael@0: } else { michael@0: result.setLong(0); michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: NFSubstitution::isNullSubstitution() const { michael@0: return FALSE; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if this is a modulus substitution. (We didn't do this michael@0: * with instanceof partially because it causes source files to michael@0: * proliferate and partially because we have to port this to C++.) michael@0: * @return true if this object is an instance of ModulusSubstitution michael@0: */ michael@0: UBool michael@0: NFSubstitution::isModulusSubstitution() const { michael@0: return FALSE; michael@0: } michael@0: michael@0: //=================================================================== michael@0: // SameValueSubstitution michael@0: //=================================================================== michael@0: michael@0: /** michael@0: * A substitution that passes the value passed to it through unchanged. michael@0: * Represented by == in rule descriptions. michael@0: */ michael@0: SameValueSubstitution::SameValueSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) michael@0: { michael@0: if (0 == description.compare(gEqualsEquals, 2)) { michael@0: // throw new IllegalArgumentException("== is not a legal token"); michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: } michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) michael@0: michael@0: //=================================================================== michael@0: // MultiplierSubstitution michael@0: //=================================================================== michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) michael@0: michael@0: UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const michael@0: { michael@0: return NFSubstitution::operator==(rhs) && michael@0: divisor == ((const MultiplierSubstitution*)&rhs)->divisor; michael@0: } michael@0: michael@0: michael@0: //=================================================================== michael@0: // ModulusSubstitution michael@0: //=================================================================== michael@0: michael@0: /** michael@0: * A substitution that divides the number being formatted by the its rule's michael@0: * divisor and formats the remainder. Represented by ">>" in a michael@0: * regular rule. michael@0: */ michael@0: ModulusSubstitution::ModulusSubstitution(int32_t _pos, michael@0: double _divisor, michael@0: const NFRule* predecessor, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) michael@0: , divisor(_divisor) michael@0: , ruleToUse(NULL) michael@0: { michael@0: ldivisor = util64_fromDouble(_divisor); michael@0: michael@0: // the owning rule's divisor controls the behavior of this michael@0: // substitution: rather than keeping a backpointer to the rule, michael@0: // we keep a copy of the divisor michael@0: michael@0: if (ldivisor == 0) { michael@0: status = U_PARSE_ERROR; michael@0: } michael@0: michael@0: if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { michael@0: // the >>> token doesn't alter how this substituion calculates the michael@0: // values it uses for formatting and parsing, but it changes michael@0: // what's done with that value after it's obtained: >>> short- michael@0: // circuits the rule-search process and goes straight to the michael@0: // specified rule to format the substitution value michael@0: ruleToUse = predecessor; michael@0: } michael@0: } michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) michael@0: michael@0: UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const michael@0: { michael@0: return NFSubstitution::operator==(rhs) && michael@0: divisor == ((const ModulusSubstitution*)&rhs)->divisor && michael@0: ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // formatting michael@0: //----------------------------------------------------------------------- michael@0: michael@0: michael@0: /** michael@0: * If this is a >>> substitution, use ruleToUse to fill in michael@0: * the substitution. Otherwise, just use the superclass function. michael@0: * @param number The number being formatted michael@0: * @toInsertInto The string to insert the result of this substitution michael@0: * into michael@0: * @param pos The position of the rule text in toInsertInto michael@0: */ michael@0: void michael@0: ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const michael@0: { michael@0: // if this isn't a >>> substitution, just use the inherited version michael@0: // of this function (which uses either a rule set or a DecimalFormat michael@0: // to format its substitution value) michael@0: if (ruleToUse == NULL) { michael@0: NFSubstitution::doSubstitution(number, toInsertInto, _pos); michael@0: michael@0: // a >>> substitution goes straight to a particular rule to michael@0: // format the substitution value michael@0: } else { michael@0: int64_t numberToFormat = transformNumber(number); michael@0: ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * If this is a >>> substitution, use ruleToUse to fill in michael@0: * the substitution. Otherwise, just use the superclass function. michael@0: * @param number The number being formatted michael@0: * @toInsertInto The string to insert the result of this substitution michael@0: * into michael@0: * @param pos The position of the rule text in toInsertInto michael@0: */ michael@0: void michael@0: ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const michael@0: { michael@0: // if this isn't a >>> substitution, just use the inherited version michael@0: // of this function (which uses either a rule set or a DecimalFormat michael@0: // to format its substitution value) michael@0: if (ruleToUse == NULL) { michael@0: NFSubstitution::doSubstitution(number, toInsertInto, _pos); michael@0: michael@0: // a >>> substitution goes straight to a particular rule to michael@0: // format the substitution value michael@0: } else { michael@0: double numberToFormat = transformNumber(number); michael@0: michael@0: ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // parsing michael@0: //----------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * If this is a >>> substitution, match only against ruleToUse. michael@0: * Otherwise, use the superclass function. michael@0: * @param text The string to parse michael@0: * @param parsePosition Ignored on entry, updated on exit to point to michael@0: * the first unmatched character. michael@0: * @param baseValue The partial parse result prior to calling this michael@0: * routine. michael@0: */ michael@0: UBool michael@0: ModulusSubstitution::doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool lenientParse, michael@0: Formattable& result) const michael@0: { michael@0: // if this isn't a >>> substitution, we can just use the michael@0: // inherited parse() routine to do the parsing michael@0: if (ruleToUse == NULL) { michael@0: return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result); michael@0: michael@0: // but if it IS a >>> substitution, we have to do it here: we michael@0: // use the specific rule's doParse() method, and then we have to michael@0: // do some of the other work of NFRuleSet.parse() michael@0: } else { michael@0: ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result); michael@0: michael@0: if (parsePosition.getIndex() != 0) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: double tempResult = result.getDouble(status); michael@0: tempResult = composeRuleValue(tempResult, baseValue); michael@0: result.setDouble(tempResult); michael@0: } michael@0: michael@0: return TRUE; michael@0: } michael@0: } michael@0: /** michael@0: * Returns a textual description of the substitution michael@0: * @return A textual description of the substitution. This might michael@0: * not be identical to the description it was created from, but michael@0: * it'll produce the same result. michael@0: */ michael@0: void michael@0: ModulusSubstitution::toString(UnicodeString& text) const michael@0: { michael@0: // use tokenChar() to get the character at the beginning and michael@0: // end of the substitutin token. In between them will go michael@0: // either the name of the rule set it uses, or the pattern of michael@0: // the DecimalFormat it uses michael@0: michael@0: if ( ruleToUse != NULL ) { // Must have been a >>> substitution. michael@0: text.remove(); michael@0: text.append(tokenChar()); michael@0: text.append(tokenChar()); michael@0: text.append(tokenChar()); michael@0: } else { // Otherwise just use the super-class function. michael@0: NFSubstitution::toString(text); michael@0: } michael@0: } michael@0: //=================================================================== michael@0: // IntegralPartSubstitution michael@0: //=================================================================== michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) michael@0: michael@0: michael@0: //=================================================================== michael@0: // FractionalPartSubstitution michael@0: //=================================================================== michael@0: michael@0: michael@0: /** michael@0: * Constructs a FractionalPartSubstitution. This object keeps a flag michael@0: * telling whether it should format by digits or not. In addition, michael@0: * it marks the rule set it calls (if any) as a fraction rule set. michael@0: */ michael@0: FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, michael@0: const NFRuleSet* _ruleSet, michael@0: const RuleBasedNumberFormat* formatter, michael@0: const UnicodeString& description, michael@0: UErrorCode& status) michael@0: : NFSubstitution(_pos, _ruleSet, formatter, description, status) michael@0: , byDigits(FALSE) michael@0: , useSpaces(TRUE) michael@0: michael@0: { michael@0: // akk, ruleSet can change in superclass constructor michael@0: if (0 == description.compare(gGreaterGreaterThan, 2) || michael@0: 0 == description.compare(gGreaterGreaterGreaterThan, 3) || michael@0: _ruleSet == getRuleSet()) { michael@0: byDigits = TRUE; michael@0: if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { michael@0: useSpaces = FALSE; michael@0: } michael@0: } else { michael@0: // cast away const michael@0: ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // formatting michael@0: //----------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * If in "by digits" mode, fills in the substitution one decimal digit michael@0: * at a time using the rule set containing this substitution. michael@0: * Otherwise, uses the superclass function. michael@0: * @param number The number being formatted michael@0: * @param toInsertInto The string to insert the result of formatting michael@0: * the substitution into michael@0: * @param pos The position of the owning rule's rule text in michael@0: * toInsertInto michael@0: */ michael@0: void michael@0: FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const michael@0: { michael@0: // if we're not in "byDigits" mode, just use the inherited michael@0: // doSubstitution() routine michael@0: if (!byDigits) { michael@0: NFSubstitution::doSubstitution(number, toInsertInto, _pos); michael@0: michael@0: // if we're in "byDigits" mode, transform the value into an integer michael@0: // by moving the decimal point eight places to the right and michael@0: // pulling digits off the right one at a time, formatting each digit michael@0: // as an integer using this substitution's owning rule set michael@0: // (this is slower, but more accurate, than doing it from the michael@0: // other end) michael@0: } else { michael@0: // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); michael@0: // // this flag keeps us from formatting trailing zeros. It starts michael@0: // // out false because we're pulling from the right, and switches michael@0: // // to true the first time we encounter a non-zero digit michael@0: // UBool doZeros = FALSE; michael@0: // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { michael@0: // int64_t digit = numberToFormat % 10; michael@0: // if (digit != 0 || doZeros) { michael@0: // if (doZeros && useSpaces) { michael@0: // toInsertInto.insert(_pos + getPos(), gSpace); michael@0: // } michael@0: // doZeros = TRUE; michael@0: // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); michael@0: // } michael@0: // numberToFormat /= 10; michael@0: // } michael@0: michael@0: DigitList dl; michael@0: dl.set(number); michael@0: dl.roundFixedPoint(20); // round to 20 fraction digits. michael@0: dl.reduce(); // Removes any trailing zeros. michael@0: michael@0: UBool pad = FALSE; michael@0: for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { michael@0: // Loop iterates over fraction digits, starting with the LSD. michael@0: // include both real digits from the number, and zeros michael@0: // to the left of the MSD but to the right of the decimal point. michael@0: if (pad && useSpaces) { michael@0: toInsertInto.insert(_pos + getPos(), gSpace); michael@0: } else { michael@0: pad = TRUE; michael@0: } michael@0: int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; michael@0: getRuleSet()->format(digit, toInsertInto, _pos + getPos()); michael@0: } michael@0: michael@0: if (!pad) { michael@0: // hack around lack of precision in digitlist. if we would end up with michael@0: // "foo point" make sure we add a " zero" to the end. michael@0: getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------- michael@0: // parsing michael@0: //----------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * If in "by digits" mode, parses the string as if it were a string michael@0: * of individual digits; otherwise, uses the superclass function. michael@0: * @param text The string to parse michael@0: * @param parsePosition Ignored on entry, but updated on exit to point michael@0: * to the first unmatched character michael@0: * @param baseValue The partial parse result prior to entering this michael@0: * function michael@0: * @param upperBound Only consider rules with base values lower than michael@0: * this when filling in the substitution michael@0: * @param lenientParse If true, try matching the text as numerals if michael@0: * matching as words doesn't work michael@0: * @return If the match was successful, the current partial parse michael@0: * result; otherwise new Long(0). The result is either a Long or michael@0: * a Double. michael@0: */ michael@0: michael@0: UBool michael@0: FractionalPartSubstitution::doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double /*upperBound*/, michael@0: UBool lenientParse, michael@0: Formattable& resVal) const michael@0: { michael@0: // if we're not in byDigits mode, we can just use the inherited michael@0: // doParse() michael@0: if (!byDigits) { michael@0: return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); michael@0: michael@0: // if we ARE in byDigits mode, parse the text one digit at a time michael@0: // using this substitution's owning rule set (we do this by setting michael@0: // upperBound to 10 when calling doParse() ) until we reach michael@0: // nonmatching text michael@0: } else { michael@0: UnicodeString workText(text); michael@0: ParsePosition workPos(1); michael@0: double result = 0; michael@0: int32_t digit; michael@0: // double p10 = 0.1; michael@0: michael@0: DigitList dl; michael@0: NumberFormat* fmt = NULL; michael@0: while (workText.length() > 0 && workPos.getIndex() != 0) { michael@0: workPos.setIndex(0); michael@0: Formattable temp; michael@0: getRuleSet()->parse(workText, workPos, 10, temp); michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: digit = temp.getLong(status); michael@0: // digit = temp.getType() == Formattable::kLong ? michael@0: // temp.getLong() : michael@0: // (int32_t)temp.getDouble(); michael@0: michael@0: if (lenientParse && workPos.getIndex() == 0) { michael@0: if (!fmt) { michael@0: status = U_ZERO_ERROR; michael@0: fmt = NumberFormat::createInstance(status); michael@0: if (U_FAILURE(status)) { michael@0: delete fmt; michael@0: fmt = NULL; michael@0: } michael@0: } michael@0: if (fmt) { michael@0: fmt->parse(workText, temp, workPos); michael@0: digit = temp.getLong(status); michael@0: } michael@0: } michael@0: michael@0: if (workPos.getIndex() != 0) { michael@0: dl.append((char)('0' + digit)); michael@0: // result += digit * p10; michael@0: // p10 /= 10; michael@0: parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); michael@0: workText.removeBetween(0, workPos.getIndex()); michael@0: while (workText.length() > 0 && workText.charAt(0) == gSpace) { michael@0: workText.removeBetween(0, 1); michael@0: parsePosition.setIndex(parsePosition.getIndex() + 1); michael@0: } michael@0: } michael@0: } michael@0: delete fmt; michael@0: michael@0: result = dl.getCount() == 0 ? 0 : dl.getDouble(); michael@0: result = composeRuleValue(result, baseValue); michael@0: resVal.setDouble(result); michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const michael@0: { michael@0: return NFSubstitution::operator==(rhs) && michael@0: ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; michael@0: } michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) michael@0: michael@0: michael@0: //=================================================================== michael@0: // AbsoluteValueSubstitution michael@0: //=================================================================== michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) michael@0: michael@0: //=================================================================== michael@0: // NumeratorSubstitution michael@0: //=================================================================== michael@0: michael@0: void michael@0: NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const { michael@0: // perform a transformation on the number being formatted that michael@0: // is dependent on the type of substitution this is michael@0: michael@0: double numberToFormat = transformNumber(number); michael@0: int64_t longNF = util64_fromDouble(numberToFormat); michael@0: michael@0: const NFRuleSet* aruleSet = getRuleSet(); michael@0: if (withZeros && aruleSet != NULL) { michael@0: // if there are leading zeros in the decimal expansion then emit them michael@0: int64_t nf =longNF; michael@0: int32_t len = toInsertInto.length(); michael@0: while ((nf *= 10) < denominator) { michael@0: toInsertInto.insert(apos + getPos(), gSpace); michael@0: aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); michael@0: } michael@0: apos += toInsertInto.length() - len; michael@0: } michael@0: michael@0: // if the result is an integer, from here on out we work in integer michael@0: // space (saving time and memory and preserving accuracy) michael@0: if (numberToFormat == longNF && aruleSet != NULL) { michael@0: aruleSet->format(longNF, toInsertInto, apos + getPos()); michael@0: michael@0: // if the result isn't an integer, then call either our rule set's michael@0: // format() method or our DecimalFormat's format() method to michael@0: // format the result michael@0: } else { michael@0: if (aruleSet != NULL) { michael@0: aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); michael@0: } else { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: UnicodeString temp; michael@0: getNumberFormat()->format(numberToFormat, temp, status); michael@0: toInsertInto.insert(apos + getPos(), temp); michael@0: } michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: NumeratorSubstitution::doParse(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: double baseValue, michael@0: double upperBound, michael@0: UBool /*lenientParse*/, michael@0: Formattable& result) const michael@0: { michael@0: // we don't have to do anything special to do the parsing here, michael@0: // but we have to turn lenient parsing off-- if we leave it on, michael@0: // it SERIOUSLY messes up the algorithm michael@0: michael@0: // if withZeros is true, we need to count the zeros michael@0: // and use that to adjust the parse result michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t zeroCount = 0; michael@0: UnicodeString workText(text); michael@0: michael@0: if (withZeros) { michael@0: ParsePosition workPos(1); michael@0: Formattable temp; michael@0: michael@0: while (workText.length() > 0 && workPos.getIndex() != 0) { michael@0: workPos.setIndex(0); michael@0: getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all michael@0: if (workPos.getIndex() == 0) { michael@0: // we failed, either there were no more zeros, or the number was formatted with digits michael@0: // either way, we're done michael@0: break; michael@0: } michael@0: michael@0: ++zeroCount; michael@0: parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); michael@0: workText.remove(0, workPos.getIndex()); michael@0: while (workText.length() > 0 && workText.charAt(0) == gSpace) { michael@0: workText.remove(0, 1); michael@0: parsePosition.setIndex(parsePosition.getIndex() + 1); michael@0: } michael@0: } michael@0: michael@0: workText = text; michael@0: workText.remove(0, (int32_t)parsePosition.getIndex()); michael@0: parsePosition.setIndex(0); michael@0: } michael@0: michael@0: // we've parsed off the zeros, now let's parse the rest from our current position michael@0: NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result); michael@0: michael@0: if (withZeros) { michael@0: // any base value will do in this case. is there a way to michael@0: // force this to not bother trying all the base values? michael@0: michael@0: // compute the 'effective' base and prescale the value down michael@0: int64_t n = result.getLong(status); // force conversion! michael@0: int64_t d = 1; michael@0: int32_t pow = 0; michael@0: while (d <= n) { michael@0: d *= 10; michael@0: ++pow; michael@0: } michael@0: // now add the zeros michael@0: while (zeroCount > 0) { michael@0: d *= 10; michael@0: --zeroCount; michael@0: } michael@0: // d is now our true denominator michael@0: result.setDouble((double)n/(double)d); michael@0: } michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: UBool michael@0: NumeratorSubstitution::operator==(const NFSubstitution& rhs) const michael@0: { michael@0: return NFSubstitution::operator==(rhs) && michael@0: denominator == ((const NumeratorSubstitution*)&rhs)->denominator; michael@0: } michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) michael@0: michael@0: const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; michael@0: michael@0: //=================================================================== michael@0: // NullSubstitution michael@0: //=================================================================== michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution) michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: /* U_HAVE_RBNF */ michael@0: #endif michael@0: