1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/nfsubs.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1364 @@ 1.4 +/* 1.5 +****************************************************************************** 1.6 +* Copyright (C) 1997-2012, International Business Machines 1.7 +* Corporation and others. All Rights Reserved. 1.8 +****************************************************************************** 1.9 +* file name: nfsubs.cpp 1.10 +* encoding: US-ASCII 1.11 +* tab size: 8 (not used) 1.12 +* indentation:4 1.13 +* 1.14 +* Modification history 1.15 +* Date Name Comments 1.16 +* 10/11/2001 Doug Ported from ICU4J 1.17 +*/ 1.18 + 1.19 +#include <stdio.h> 1.20 +#include "utypeinfo.h" // for 'typeid' to work 1.21 + 1.22 +#include "nfsubs.h" 1.23 +#include "digitlst.h" 1.24 + 1.25 +#if U_HAVE_RBNF 1.26 + 1.27 +static const UChar gLessThan = 0x003c; 1.28 +static const UChar gEquals = 0x003d; 1.29 +static const UChar gGreaterThan = 0x003e; 1.30 +static const UChar gPercent = 0x0025; 1.31 +static const UChar gPound = 0x0023; 1.32 +static const UChar gZero = 0x0030; 1.33 +static const UChar gSpace = 0x0020; 1.34 + 1.35 +static const UChar gEqualsEquals[] = 1.36 +{ 1.37 + 0x3D, 0x3D, 0 1.38 +}; /* "==" */ 1.39 +static const UChar gGreaterGreaterGreaterThan[] = 1.40 +{ 1.41 + 0x3E, 0x3E, 0x3E, 0 1.42 +}; /* ">>>" */ 1.43 +static const UChar gGreaterGreaterThan[] = 1.44 +{ 1.45 + 0x3E, 0x3E, 0 1.46 +}; /* ">>" */ 1.47 + 1.48 +U_NAMESPACE_BEGIN 1.49 + 1.50 +class SameValueSubstitution : public NFSubstitution { 1.51 +public: 1.52 + SameValueSubstitution(int32_t pos, 1.53 + const NFRuleSet* ruleset, 1.54 + const RuleBasedNumberFormat* formatter, 1.55 + const UnicodeString& description, 1.56 + UErrorCode& status); 1.57 + virtual ~SameValueSubstitution(); 1.58 + 1.59 + virtual int64_t transformNumber(int64_t number) const { return number; } 1.60 + virtual double transformNumber(double number) const { return number; } 1.61 + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return newRuleValue; } 1.62 + virtual double calcUpperBound(double oldUpperBound) const { return oldUpperBound; } 1.63 + virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' 1.64 + 1.65 +public: 1.66 + static UClassID getStaticClassID(void); 1.67 + virtual UClassID getDynamicClassID(void) const; 1.68 +}; 1.69 + 1.70 +SameValueSubstitution::~SameValueSubstitution() {} 1.71 + 1.72 +class MultiplierSubstitution : public NFSubstitution { 1.73 + double divisor; 1.74 + int64_t ldivisor; 1.75 + 1.76 +public: 1.77 + MultiplierSubstitution(int32_t _pos, 1.78 + double _divisor, 1.79 + const NFRuleSet* _ruleSet, 1.80 + const RuleBasedNumberFormat* formatter, 1.81 + const UnicodeString& description, 1.82 + UErrorCode& status) 1.83 + : NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor) 1.84 + { 1.85 + ldivisor = util64_fromDouble(divisor); 1.86 + if (divisor == 0) { 1.87 + status = U_PARSE_ERROR; 1.88 + } 1.89 + } 1.90 + virtual ~MultiplierSubstitution(); 1.91 + 1.92 + virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { 1.93 + divisor = uprv_pow(radix, exponent); 1.94 + ldivisor = util64_fromDouble(divisor); 1.95 + 1.96 + if(divisor == 0) { 1.97 + status = U_PARSE_ERROR; 1.98 + } 1.99 + } 1.100 + 1.101 + virtual UBool operator==(const NFSubstitution& rhs) const; 1.102 + 1.103 + virtual int64_t transformNumber(int64_t number) const { 1.104 + return number / ldivisor; 1.105 + } 1.106 + 1.107 + virtual double transformNumber(double number) const { 1.108 + if (getRuleSet()) { 1.109 + return uprv_floor(number / divisor); 1.110 + } else { 1.111 + return number/divisor; 1.112 + } 1.113 + } 1.114 + 1.115 + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { 1.116 + return newRuleValue * divisor; 1.117 + } 1.118 + 1.119 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } 1.120 + 1.121 + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 1.122 + 1.123 +public: 1.124 + static UClassID getStaticClassID(void); 1.125 + virtual UClassID getDynamicClassID(void) const; 1.126 +}; 1.127 + 1.128 +MultiplierSubstitution::~MultiplierSubstitution() {} 1.129 + 1.130 +class ModulusSubstitution : public NFSubstitution { 1.131 + double divisor; 1.132 + int64_t ldivisor; 1.133 + const NFRule* ruleToUse; 1.134 +public: 1.135 + ModulusSubstitution(int32_t pos, 1.136 + double _divisor, 1.137 + const NFRule* rulePredecessor, 1.138 + const NFRuleSet* ruleSet, 1.139 + const RuleBasedNumberFormat* formatter, 1.140 + const UnicodeString& description, 1.141 + UErrorCode& status); 1.142 + virtual ~ModulusSubstitution(); 1.143 + 1.144 + virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status) { 1.145 + divisor = uprv_pow(radix, exponent); 1.146 + ldivisor = util64_fromDouble(divisor); 1.147 + 1.148 + if (divisor == 0) { 1.149 + status = U_PARSE_ERROR; 1.150 + } 1.151 + } 1.152 + 1.153 + virtual UBool operator==(const NFSubstitution& rhs) const; 1.154 + 1.155 + virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos) const; 1.156 + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; 1.157 + 1.158 + virtual int64_t transformNumber(int64_t number) const { return number % ldivisor; } 1.159 + virtual double transformNumber(double number) const { return uprv_fmod(number, divisor); } 1.160 + 1.161 + virtual UBool doParse(const UnicodeString& text, 1.162 + ParsePosition& parsePosition, 1.163 + double baseValue, 1.164 + double upperBound, 1.165 + UBool lenientParse, 1.166 + Formattable& result) const; 1.167 + 1.168 + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { 1.169 + return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue; 1.170 + } 1.171 + 1.172 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return divisor; } 1.173 + 1.174 + virtual UBool isModulusSubstitution() const { return TRUE; } 1.175 + 1.176 + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 1.177 + 1.178 + virtual void toString(UnicodeString& result) const; 1.179 + 1.180 +public: 1.181 + static UClassID getStaticClassID(void); 1.182 + virtual UClassID getDynamicClassID(void) const; 1.183 +}; 1.184 + 1.185 +ModulusSubstitution::~ModulusSubstitution() {} 1.186 + 1.187 +class IntegralPartSubstitution : public NFSubstitution { 1.188 +public: 1.189 + IntegralPartSubstitution(int32_t _pos, 1.190 + const NFRuleSet* _ruleSet, 1.191 + const RuleBasedNumberFormat* formatter, 1.192 + const UnicodeString& description, 1.193 + UErrorCode& status) 1.194 + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} 1.195 + virtual ~IntegralPartSubstitution(); 1.196 + 1.197 + virtual int64_t transformNumber(int64_t number) const { return number; } 1.198 + virtual double transformNumber(double number) const { return uprv_floor(number); } 1.199 + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } 1.200 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } 1.201 + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 1.202 + 1.203 +public: 1.204 + static UClassID getStaticClassID(void); 1.205 + virtual UClassID getDynamicClassID(void) const; 1.206 +}; 1.207 + 1.208 +IntegralPartSubstitution::~IntegralPartSubstitution() {} 1.209 + 1.210 +class FractionalPartSubstitution : public NFSubstitution { 1.211 + UBool byDigits; 1.212 + UBool useSpaces; 1.213 + enum { kMaxDecimalDigits = 8 }; 1.214 +public: 1.215 + FractionalPartSubstitution(int32_t pos, 1.216 + const NFRuleSet* ruleSet, 1.217 + const RuleBasedNumberFormat* formatter, 1.218 + const UnicodeString& description, 1.219 + UErrorCode& status); 1.220 + virtual ~FractionalPartSubstitution(); 1.221 + 1.222 + virtual UBool operator==(const NFSubstitution& rhs) const; 1.223 + 1.224 + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; 1.225 + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} 1.226 + virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } 1.227 + virtual double transformNumber(double number) const { return number - uprv_floor(number); } 1.228 + 1.229 + virtual UBool doParse(const UnicodeString& text, 1.230 + ParsePosition& parsePosition, 1.231 + double baseValue, 1.232 + double upperBound, 1.233 + UBool lenientParse, 1.234 + Formattable& result) const; 1.235 + 1.236 + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; } 1.237 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0; } 1.238 + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 1.239 + 1.240 +public: 1.241 + static UClassID getStaticClassID(void); 1.242 + virtual UClassID getDynamicClassID(void) const; 1.243 +}; 1.244 + 1.245 +FractionalPartSubstitution::~FractionalPartSubstitution() {} 1.246 + 1.247 +class AbsoluteValueSubstitution : public NFSubstitution { 1.248 +public: 1.249 + AbsoluteValueSubstitution(int32_t _pos, 1.250 + const NFRuleSet* _ruleSet, 1.251 + const RuleBasedNumberFormat* formatter, 1.252 + const UnicodeString& description, 1.253 + UErrorCode& status) 1.254 + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} 1.255 + virtual ~AbsoluteValueSubstitution(); 1.256 + 1.257 + virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; } 1.258 + virtual double transformNumber(double number) const { return uprv_fabs(number); } 1.259 + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const { return -newRuleValue; } 1.260 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_MAX; } 1.261 + virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' 1.262 + 1.263 +public: 1.264 + static UClassID getStaticClassID(void); 1.265 + virtual UClassID getDynamicClassID(void) const; 1.266 +}; 1.267 + 1.268 +AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} 1.269 + 1.270 +class NumeratorSubstitution : public NFSubstitution { 1.271 + double denominator; 1.272 + int64_t ldenominator; 1.273 + UBool withZeros; 1.274 +public: 1.275 + static inline UnicodeString fixdesc(const UnicodeString& desc) { 1.276 + if (desc.endsWith(LTLT, 2)) { 1.277 + UnicodeString result(desc, 0, desc.length()-1); 1.278 + return result; 1.279 + } 1.280 + return desc; 1.281 + } 1.282 + NumeratorSubstitution(int32_t _pos, 1.283 + double _denominator, 1.284 + const NFRuleSet* _ruleSet, 1.285 + const RuleBasedNumberFormat* formatter, 1.286 + const UnicodeString& description, 1.287 + UErrorCode& status) 1.288 + : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator) 1.289 + { 1.290 + ldenominator = util64_fromDouble(denominator); 1.291 + withZeros = description.endsWith(LTLT, 2); 1.292 + } 1.293 + virtual ~NumeratorSubstitution(); 1.294 + 1.295 + virtual UBool operator==(const NFSubstitution& rhs) const; 1.296 + 1.297 + virtual int64_t transformNumber(int64_t number) const { return number * ldenominator; } 1.298 + virtual double transformNumber(double number) const { return uprv_round(number * denominator); } 1.299 + 1.300 + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} 1.301 + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos) const; 1.302 + virtual UBool doParse(const UnicodeString& text, 1.303 + ParsePosition& parsePosition, 1.304 + double baseValue, 1.305 + double upperBound, 1.306 + UBool /*lenientParse*/, 1.307 + Formattable& result) const; 1.308 + 1.309 + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; } 1.310 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return denominator; } 1.311 + virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' 1.312 +private: 1.313 + static const UChar LTLT[2]; 1.314 + 1.315 +public: 1.316 + static UClassID getStaticClassID(void); 1.317 + virtual UClassID getDynamicClassID(void) const; 1.318 +}; 1.319 + 1.320 +NumeratorSubstitution::~NumeratorSubstitution() {} 1.321 + 1.322 +class NullSubstitution : public NFSubstitution { 1.323 +public: 1.324 + NullSubstitution(int32_t _pos, 1.325 + const NFRuleSet* _ruleSet, 1.326 + const RuleBasedNumberFormat* formatter, 1.327 + const UnicodeString& description, 1.328 + UErrorCode& status) 1.329 + : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} 1.330 + virtual ~NullSubstitution(); 1.331 + 1.332 + virtual void toString(UnicodeString& /*result*/) const {} 1.333 + virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} 1.334 + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/) const {} 1.335 + virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } 1.336 + virtual double transformNumber(double /*number*/) const { return 0; } 1.337 + virtual UBool doParse(const UnicodeString& /*text*/, 1.338 + ParsePosition& /*parsePosition*/, 1.339 + double baseValue, 1.340 + double /*upperBound*/, 1.341 + UBool /*lenientParse*/, 1.342 + Formattable& result) const 1.343 + { result.setDouble(baseValue); return TRUE; } 1.344 + virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called 1.345 + virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called 1.346 + virtual UBool isNullSubstitution() const { return TRUE; } 1.347 + virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never called 1.348 + 1.349 +public: 1.350 + static UClassID getStaticClassID(void); 1.351 + virtual UClassID getDynamicClassID(void) const; 1.352 +}; 1.353 + 1.354 +NullSubstitution::~NullSubstitution() {} 1.355 + 1.356 +NFSubstitution* 1.357 +NFSubstitution::makeSubstitution(int32_t pos, 1.358 + const NFRule* rule, 1.359 + const NFRule* predecessor, 1.360 + const NFRuleSet* ruleSet, 1.361 + const RuleBasedNumberFormat* formatter, 1.362 + const UnicodeString& description, 1.363 + UErrorCode& status) 1.364 +{ 1.365 + // if the description is empty, return a NullSubstitution 1.366 + if (description.length() == 0) { 1.367 + return new NullSubstitution(pos, ruleSet, formatter, description, status); 1.368 + } 1.369 + 1.370 + switch (description.charAt(0)) { 1.371 + // if the description begins with '<'... 1.372 + case gLessThan: 1.373 + // throw an exception if the rule is a negative number 1.374 + // rule 1.375 + if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { 1.376 + // throw new IllegalArgumentException("<< not allowed in negative-number rule"); 1.377 + status = U_PARSE_ERROR; 1.378 + return NULL; 1.379 + } 1.380 + 1.381 + // if the rule is a fraction rule, return an 1.382 + // IntegralPartSubstitution 1.383 + else if (rule->getBaseValue() == NFRule::kImproperFractionRule 1.384 + || rule->getBaseValue() == NFRule::kProperFractionRule 1.385 + || rule->getBaseValue() == NFRule::kMasterRule) { 1.386 + return new IntegralPartSubstitution(pos, ruleSet, formatter, description, status); 1.387 + } 1.388 + 1.389 + // if the rule set containing the rule is a fraction 1.390 + // rule set, return a NumeratorSubstitution 1.391 + else if (ruleSet->isFractionRuleSet()) { 1.392 + return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), 1.393 + formatter->getDefaultRuleSet(), formatter, description, status); 1.394 + } 1.395 + 1.396 + // otherwise, return a MultiplierSubstitution 1.397 + else { 1.398 + return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet, 1.399 + formatter, description, status); 1.400 + } 1.401 + 1.402 + // if the description begins with '>'... 1.403 + case gGreaterThan: 1.404 + // if the rule is a negative-number rule, return 1.405 + // an AbsoluteValueSubstitution 1.406 + if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { 1.407 + return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description, status); 1.408 + } 1.409 + 1.410 + // if the rule is a fraction rule, return a 1.411 + // FractionalPartSubstitution 1.412 + else if (rule->getBaseValue() == NFRule::kImproperFractionRule 1.413 + || rule->getBaseValue() == NFRule::kProperFractionRule 1.414 + || rule->getBaseValue() == NFRule::kMasterRule) { 1.415 + return new FractionalPartSubstitution(pos, ruleSet, formatter, description, status); 1.416 + } 1.417 + 1.418 + // if the rule set owning the rule is a fraction rule set, 1.419 + // throw an exception 1.420 + else if (ruleSet->isFractionRuleSet()) { 1.421 + // throw new IllegalArgumentException(">> not allowed in fraction rule set"); 1.422 + status = U_PARSE_ERROR; 1.423 + return NULL; 1.424 + } 1.425 + 1.426 + // otherwise, return a ModulusSubstitution 1.427 + else { 1.428 + return new ModulusSubstitution(pos, rule->getDivisor(), predecessor, 1.429 + ruleSet, formatter, description, status); 1.430 + } 1.431 + 1.432 + // if the description begins with '=', always return a 1.433 + // SameValueSubstitution 1.434 + case gEquals: 1.435 + return new SameValueSubstitution(pos, ruleSet, formatter, description, status); 1.436 + 1.437 + // and if it's anything else, throw an exception 1.438 + default: 1.439 + // throw new IllegalArgumentException("Illegal substitution character"); 1.440 + status = U_PARSE_ERROR; 1.441 + } 1.442 + return NULL; 1.443 +} 1.444 + 1.445 +NFSubstitution::NFSubstitution(int32_t _pos, 1.446 + const NFRuleSet* _ruleSet, 1.447 + const RuleBasedNumberFormat* formatter, 1.448 + const UnicodeString& description, 1.449 + UErrorCode& status) 1.450 + : pos(_pos), ruleSet(NULL), numberFormat(NULL) 1.451 +{ 1.452 + // the description should begin and end with the same character. 1.453 + // If it doesn't that's a syntax error. Otherwise, 1.454 + // makeSubstitution() was the only thing that needed to know 1.455 + // about these characters, so strip them off 1.456 + UnicodeString workingDescription(description); 1.457 + if (description.length() >= 2 1.458 + && description.charAt(0) == description.charAt(description.length() - 1)) 1.459 + { 1.460 + workingDescription.remove(description.length() - 1, 1); 1.461 + workingDescription.remove(0, 1); 1.462 + } 1.463 + else if (description.length() != 0) { 1.464 + // throw new IllegalArgumentException("Illegal substitution syntax"); 1.465 + status = U_PARSE_ERROR; 1.466 + return; 1.467 + } 1.468 + 1.469 + // if the description was just two paired token characters 1.470 + // (i.e., "<<" or ">>"), it uses the rule set it belongs to to 1.471 + // format its result 1.472 + if (workingDescription.length() == 0) { 1.473 + this->ruleSet = _ruleSet; 1.474 + } 1.475 + // if the description contains a rule set name, that's the rule 1.476 + // set we use to format the result: get a reference to the 1.477 + // names rule set 1.478 + else if (workingDescription.charAt(0) == gPercent) { 1.479 + this->ruleSet = formatter->findRuleSet(workingDescription, status); 1.480 + } 1.481 + // if the description begins with 0 or #, treat it as a 1.482 + // DecimalFormat pattern, and initialize a DecimalFormat with 1.483 + // that pattern (then set it to use the DecimalFormatSymbols 1.484 + // belonging to our formatter) 1.485 + else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { 1.486 + DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols(); 1.487 + if (!sym) { 1.488 + status = U_MISSING_RESOURCE_ERROR; 1.489 + return; 1.490 + } 1.491 + this->numberFormat = new DecimalFormat(workingDescription, *sym, status); 1.492 + /* test for NULL */ 1.493 + if (this->numberFormat == 0) { 1.494 + status = U_MEMORY_ALLOCATION_ERROR; 1.495 + return; 1.496 + } 1.497 + if (U_FAILURE(status)) { 1.498 + delete (DecimalFormat*)this->numberFormat; 1.499 + this->numberFormat = NULL; 1.500 + return; 1.501 + } 1.502 + // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols()); 1.503 + } 1.504 + // if the description is ">>>", this substitution bypasses the 1.505 + // usual rule-search process and always uses the rule that precedes 1.506 + // it in its own rule set's rule list (this is used for place-value 1.507 + // notations: formats where you want to see a particular part of 1.508 + // a number even when it's 0) 1.509 + else if (workingDescription.charAt(0) == gGreaterThan) { 1.510 + // this causes problems when >>> is used in a frationalPartSubstitution 1.511 + // this->ruleSet = NULL; 1.512 + this->ruleSet = _ruleSet; 1.513 + this->numberFormat = NULL; 1.514 + } 1.515 + // and of the description is none of these things, it's a syntax error 1.516 + else { 1.517 + // throw new IllegalArgumentException("Illegal substitution syntax"); 1.518 + status = U_PARSE_ERROR; 1.519 + } 1.520 +} 1.521 + 1.522 +NFSubstitution::~NFSubstitution() 1.523 +{ 1.524 + // cast away const 1.525 + delete (NumberFormat*)numberFormat; numberFormat = NULL; 1.526 +} 1.527 + 1.528 +/** 1.529 + * Set's the substitution's divisor. Used by NFRule.setBaseValue(). 1.530 + * A no-op for all substitutions except multiplier and modulus 1.531 + * substitutions. 1.532 + * @param radix The radix of the divisor 1.533 + * @param exponent The exponent of the divisor 1.534 + */ 1.535 +void 1.536 +NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode& /*status*/) { 1.537 + // a no-op for all substitutions except multiplier and modulus substitutions 1.538 +} 1.539 + 1.540 + 1.541 +//----------------------------------------------------------------------- 1.542 +// boilerplate 1.543 +//----------------------------------------------------------------------- 1.544 + 1.545 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) 1.546 + 1.547 +/** 1.548 + * Compares two substitutions for equality 1.549 + * @param The substitution to compare this one to 1.550 + * @return true if the two substitutions are functionally equivalent 1.551 + */ 1.552 +UBool 1.553 +NFSubstitution::operator==(const NFSubstitution& rhs) const 1.554 +{ 1.555 + // compare class and all of the fields all substitutions have 1.556 + // in common 1.557 + // this should be called by subclasses before their own equality tests 1.558 + return typeid(*this) == typeid(rhs) 1.559 + && pos == rhs.pos 1.560 + && (ruleSet == NULL) == (rhs.ruleSet == NULL) 1.561 + // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? 1.562 + && (numberFormat == NULL 1.563 + ? (rhs.numberFormat == NULL) 1.564 + : (*numberFormat == *rhs.numberFormat)); 1.565 +} 1.566 + 1.567 +/** 1.568 + * Returns a textual description of the substitution 1.569 + * @return A textual description of the substitution. This might 1.570 + * not be identical to the description it was created from, but 1.571 + * it'll produce the same result. 1.572 + */ 1.573 +void 1.574 +NFSubstitution::toString(UnicodeString& text) const 1.575 +{ 1.576 + // use tokenChar() to get the character at the beginning and 1.577 + // end of the substitutin token. In between them will go 1.578 + // either the name of the rule set it uses, or the pattern of 1.579 + // the DecimalFormat it uses 1.580 + text.remove(); 1.581 + text.append(tokenChar()); 1.582 + 1.583 + UnicodeString temp; 1.584 + if (ruleSet != NULL) { 1.585 + ruleSet->getName(temp); 1.586 + } else if (numberFormat != NULL) { 1.587 + numberFormat->toPattern(temp); 1.588 + } 1.589 + text.append(temp); 1.590 + text.append(tokenChar()); 1.591 +} 1.592 + 1.593 +//----------------------------------------------------------------------- 1.594 +// formatting 1.595 +//----------------------------------------------------------------------- 1.596 + 1.597 +/** 1.598 + * Performs a mathematical operation on the number, formats it using 1.599 + * either ruleSet or decimalFormat, and inserts the result into 1.600 + * toInsertInto. 1.601 + * @param number The number being formatted. 1.602 + * @param toInsertInto The string we insert the result into 1.603 + * @param pos The position in toInsertInto where the owning rule's 1.604 + * rule text begins (this value is added to this substitution's 1.605 + * position to determine exactly where to insert the new text) 1.606 + */ 1.607 +void 1.608 +NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const 1.609 +{ 1.610 + if (ruleSet != NULL) { 1.611 + // perform a transformation on the number that is dependent 1.612 + // on the type of substitution this is, then just call its 1.613 + // rule set's format() method to format the result 1.614 + ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos); 1.615 + } else if (numberFormat != NULL) { 1.616 + // or perform the transformation on the number (preserving 1.617 + // the result's fractional part if the formatter it set 1.618 + // to show it), then use that formatter's format() method 1.619 + // to format the result 1.620 + double numberToFormat = transformNumber((double)number); 1.621 + if (numberFormat->getMaximumFractionDigits() == 0) { 1.622 + numberToFormat = uprv_floor(numberToFormat); 1.623 + } 1.624 + 1.625 + UnicodeString temp; 1.626 + numberFormat->format(numberToFormat, temp); 1.627 + toInsertInto.insert(_pos + this->pos, temp); 1.628 + } 1.629 +} 1.630 + 1.631 +/** 1.632 + * Performs a mathematical operation on the number, formats it using 1.633 + * either ruleSet or decimalFormat, and inserts the result into 1.634 + * toInsertInto. 1.635 + * @param number The number being formatted. 1.636 + * @param toInsertInto The string we insert the result into 1.637 + * @param pos The position in toInsertInto where the owning rule's 1.638 + * rule text begins (this value is added to this substitution's 1.639 + * position to determine exactly where to insert the new text) 1.640 + */ 1.641 +void 1.642 +NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { 1.643 + // perform a transformation on the number being formatted that 1.644 + // is dependent on the type of substitution this is 1.645 + double numberToFormat = transformNumber(number); 1.646 + 1.647 + // if the result is an integer, from here on out we work in integer 1.648 + // space (saving time and memory and preserving accuracy) 1.649 + if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { 1.650 + ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos); 1.651 + 1.652 + // if the result isn't an integer, then call either our rule set's 1.653 + // format() method or our DecimalFormat's format() method to 1.654 + // format the result 1.655 + } else { 1.656 + if (ruleSet != NULL) { 1.657 + ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos); 1.658 + } else if (numberFormat != NULL) { 1.659 + UnicodeString temp; 1.660 + numberFormat->format(numberToFormat, temp); 1.661 + toInsertInto.insert(_pos + this->pos, temp); 1.662 + } 1.663 + } 1.664 +} 1.665 + 1.666 + 1.667 + //----------------------------------------------------------------------- 1.668 + // parsing 1.669 + //----------------------------------------------------------------------- 1.670 + 1.671 +#ifdef RBNF_DEBUG 1.672 +#include <stdio.h> 1.673 +#endif 1.674 + 1.675 +/** 1.676 + * Parses a string using the rule set or DecimalFormat belonging 1.677 + * to this substitution. If there's a match, a mathematical 1.678 + * operation (the inverse of the one used in formatting) is 1.679 + * performed on the result of the parse and the value passed in 1.680 + * and returned as the result. The parse position is updated to 1.681 + * point to the first unmatched character in the string. 1.682 + * @param text The string to parse 1.683 + * @param parsePosition On entry, ignored, but assumed to be 0. 1.684 + * On exit, this is updated to point to the first unmatched 1.685 + * character (or 0 if the substitution didn't match) 1.686 + * @param baseValue A partial parse result that should be 1.687 + * combined with the result of this parse 1.688 + * @param upperBound When searching the rule set for a rule 1.689 + * matching the string passed in, only rules with base values 1.690 + * lower than this are considered 1.691 + * @param lenientParse If true and matching against rules fails, 1.692 + * the substitution will also try matching the text against 1.693 + * numerals using a default-costructed NumberFormat. If false, 1.694 + * no extra work is done. (This value is false whenever the 1.695 + * formatter isn't in lenient-parse mode, but is also false 1.696 + * under some conditions even when the formatter _is_ in 1.697 + * lenient-parse mode.) 1.698 + * @return If there's a match, this is the result of composing 1.699 + * baseValue with whatever was returned from matching the 1.700 + * characters. This will be either a Long or a Double. If there's 1.701 + * no match this is new Long(0) (not null), and parsePosition 1.702 + * is left unchanged. 1.703 + */ 1.704 +UBool 1.705 +NFSubstitution::doParse(const UnicodeString& text, 1.706 + ParsePosition& parsePosition, 1.707 + double baseValue, 1.708 + double upperBound, 1.709 + UBool lenientParse, 1.710 + Formattable& result) const 1.711 +{ 1.712 +#ifdef RBNF_DEBUG 1.713 + fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound); 1.714 +#endif 1.715 + // figure out the highest base value a rule can have and match 1.716 + // the text being parsed (this varies according to the type of 1.717 + // substitutions: multiplier, modulus, and numerator substitutions 1.718 + // restrict the search to rules with base values lower than their 1.719 + // own; same-value substitutions leave the upper bound wherever 1.720 + // it was, and the others allow any rule to match 1.721 + upperBound = calcUpperBound(upperBound); 1.722 + 1.723 + // use our rule set to parse the text. If that fails and 1.724 + // lenient parsing is enabled (this is always false if the 1.725 + // formatter's lenient-parsing mode is off, but it may also 1.726 + // be false even when the formatter's lenient-parse mode is 1.727 + // on), then also try parsing the text using a default- 1.728 + // constructed NumberFormat 1.729 + if (ruleSet != NULL) { 1.730 + ruleSet->parse(text, parsePosition, upperBound, result); 1.731 + if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { 1.732 + UErrorCode status = U_ZERO_ERROR; 1.733 + NumberFormat* fmt = NumberFormat::createInstance(status); 1.734 + if (U_SUCCESS(status)) { 1.735 + fmt->parse(text, result, parsePosition); 1.736 + } 1.737 + delete fmt; 1.738 + } 1.739 + 1.740 + // ...or use our DecimalFormat to parse the text 1.741 + } else if (numberFormat != NULL) { 1.742 + numberFormat->parse(text, result, parsePosition); 1.743 + } 1.744 + 1.745 + // if the parse was successful, we've already advanced the caller's 1.746 + // parse position (this is the one function that doesn't have one 1.747 + // of its own). Derive a parse result and return it as a Long, 1.748 + // if possible, or a Double 1.749 + if (parsePosition.getIndex() != 0) { 1.750 + UErrorCode status = U_ZERO_ERROR; 1.751 + double tempResult = result.getDouble(status); 1.752 + 1.753 + // composeRuleValue() produces a full parse result from 1.754 + // the partial parse result passed to this function from 1.755 + // the caller (this is either the owning rule's base value 1.756 + // or the partial result obtained from composing the 1.757 + // owning rule's base value with its other substitution's 1.758 + // parse result) and the partial parse result obtained by 1.759 + // matching the substitution (which will be the same value 1.760 + // the caller would get by parsing just this part of the 1.761 + // text with RuleBasedNumberFormat.parse() ). How the two 1.762 + // values are used to derive the full parse result depends 1.763 + // on the types of substitutions: For a regular rule, the 1.764 + // ultimate result is its multiplier substitution's result 1.765 + // times the rule's divisor (or the rule's base value) plus 1.766 + // the modulus substitution's result (which will actually 1.767 + // supersede part of the rule's base value). For a negative- 1.768 + // number rule, the result is the negative of its substitution's 1.769 + // result. For a fraction rule, it's the sum of its two 1.770 + // substitution results. For a rule in a fraction rule set, 1.771 + // it's the numerator substitution's result divided by 1.772 + // the rule's base value. Results from same-value substitutions 1.773 + // propagate back upard, and null substitutions don't affect 1.774 + // the result. 1.775 + tempResult = composeRuleValue(tempResult, baseValue); 1.776 + result.setDouble(tempResult); 1.777 + return TRUE; 1.778 + // if the parse was UNsuccessful, return 0 1.779 + } else { 1.780 + result.setLong(0); 1.781 + return FALSE; 1.782 + } 1.783 +} 1.784 + 1.785 +UBool 1.786 +NFSubstitution::isNullSubstitution() const { 1.787 + return FALSE; 1.788 +} 1.789 + 1.790 + /** 1.791 + * Returns true if this is a modulus substitution. (We didn't do this 1.792 + * with instanceof partially because it causes source files to 1.793 + * proliferate and partially because we have to port this to C++.) 1.794 + * @return true if this object is an instance of ModulusSubstitution 1.795 + */ 1.796 +UBool 1.797 +NFSubstitution::isModulusSubstitution() const { 1.798 + return FALSE; 1.799 +} 1.800 + 1.801 +//=================================================================== 1.802 +// SameValueSubstitution 1.803 +//=================================================================== 1.804 + 1.805 +/** 1.806 + * A substitution that passes the value passed to it through unchanged. 1.807 + * Represented by == in rule descriptions. 1.808 + */ 1.809 +SameValueSubstitution::SameValueSubstitution(int32_t _pos, 1.810 + const NFRuleSet* _ruleSet, 1.811 + const RuleBasedNumberFormat* formatter, 1.812 + const UnicodeString& description, 1.813 + UErrorCode& status) 1.814 +: NFSubstitution(_pos, _ruleSet, formatter, description, status) 1.815 +{ 1.816 + if (0 == description.compare(gEqualsEquals, 2)) { 1.817 + // throw new IllegalArgumentException("== is not a legal token"); 1.818 + status = U_PARSE_ERROR; 1.819 + } 1.820 +} 1.821 + 1.822 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) 1.823 + 1.824 +//=================================================================== 1.825 +// MultiplierSubstitution 1.826 +//=================================================================== 1.827 + 1.828 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) 1.829 + 1.830 +UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const 1.831 +{ 1.832 + return NFSubstitution::operator==(rhs) && 1.833 + divisor == ((const MultiplierSubstitution*)&rhs)->divisor; 1.834 +} 1.835 + 1.836 + 1.837 +//=================================================================== 1.838 +// ModulusSubstitution 1.839 +//=================================================================== 1.840 + 1.841 +/** 1.842 + * A substitution that divides the number being formatted by the its rule's 1.843 + * divisor and formats the remainder. Represented by ">>" in a 1.844 + * regular rule. 1.845 + */ 1.846 +ModulusSubstitution::ModulusSubstitution(int32_t _pos, 1.847 + double _divisor, 1.848 + const NFRule* predecessor, 1.849 + const NFRuleSet* _ruleSet, 1.850 + const RuleBasedNumberFormat* formatter, 1.851 + const UnicodeString& description, 1.852 + UErrorCode& status) 1.853 + : NFSubstitution(_pos, _ruleSet, formatter, description, status) 1.854 + , divisor(_divisor) 1.855 + , ruleToUse(NULL) 1.856 +{ 1.857 + ldivisor = util64_fromDouble(_divisor); 1.858 + 1.859 + // the owning rule's divisor controls the behavior of this 1.860 + // substitution: rather than keeping a backpointer to the rule, 1.861 + // we keep a copy of the divisor 1.862 + 1.863 + if (ldivisor == 0) { 1.864 + status = U_PARSE_ERROR; 1.865 + } 1.866 + 1.867 + if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { 1.868 + // the >>> token doesn't alter how this substituion calculates the 1.869 + // values it uses for formatting and parsing, but it changes 1.870 + // what's done with that value after it's obtained: >>> short- 1.871 + // circuits the rule-search process and goes straight to the 1.872 + // specified rule to format the substitution value 1.873 + ruleToUse = predecessor; 1.874 + } 1.875 +} 1.876 + 1.877 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) 1.878 + 1.879 +UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const 1.880 +{ 1.881 + return NFSubstitution::operator==(rhs) && 1.882 + divisor == ((const ModulusSubstitution*)&rhs)->divisor && 1.883 + ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; 1.884 +} 1.885 + 1.886 +//----------------------------------------------------------------------- 1.887 +// formatting 1.888 +//----------------------------------------------------------------------- 1.889 + 1.890 + 1.891 +/** 1.892 + * If this is a >>> substitution, use ruleToUse to fill in 1.893 + * the substitution. Otherwise, just use the superclass function. 1.894 + * @param number The number being formatted 1.895 + * @toInsertInto The string to insert the result of this substitution 1.896 + * into 1.897 + * @param pos The position of the rule text in toInsertInto 1.898 + */ 1.899 +void 1.900 +ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos) const 1.901 +{ 1.902 + // if this isn't a >>> substitution, just use the inherited version 1.903 + // of this function (which uses either a rule set or a DecimalFormat 1.904 + // to format its substitution value) 1.905 + if (ruleToUse == NULL) { 1.906 + NFSubstitution::doSubstitution(number, toInsertInto, _pos); 1.907 + 1.908 + // a >>> substitution goes straight to a particular rule to 1.909 + // format the substitution value 1.910 + } else { 1.911 + int64_t numberToFormat = transformNumber(number); 1.912 + ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); 1.913 + } 1.914 +} 1.915 + 1.916 +/** 1.917 +* If this is a >>> substitution, use ruleToUse to fill in 1.918 +* the substitution. Otherwise, just use the superclass function. 1.919 +* @param number The number being formatted 1.920 +* @toInsertInto The string to insert the result of this substitution 1.921 +* into 1.922 +* @param pos The position of the rule text in toInsertInto 1.923 +*/ 1.924 +void 1.925 +ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const 1.926 +{ 1.927 + // if this isn't a >>> substitution, just use the inherited version 1.928 + // of this function (which uses either a rule set or a DecimalFormat 1.929 + // to format its substitution value) 1.930 + if (ruleToUse == NULL) { 1.931 + NFSubstitution::doSubstitution(number, toInsertInto, _pos); 1.932 + 1.933 + // a >>> substitution goes straight to a particular rule to 1.934 + // format the substitution value 1.935 + } else { 1.936 + double numberToFormat = transformNumber(number); 1.937 + 1.938 + ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); 1.939 + } 1.940 +} 1.941 + 1.942 +//----------------------------------------------------------------------- 1.943 +// parsing 1.944 +//----------------------------------------------------------------------- 1.945 + 1.946 +/** 1.947 + * If this is a >>> substitution, match only against ruleToUse. 1.948 + * Otherwise, use the superclass function. 1.949 + * @param text The string to parse 1.950 + * @param parsePosition Ignored on entry, updated on exit to point to 1.951 + * the first unmatched character. 1.952 + * @param baseValue The partial parse result prior to calling this 1.953 + * routine. 1.954 + */ 1.955 +UBool 1.956 +ModulusSubstitution::doParse(const UnicodeString& text, 1.957 + ParsePosition& parsePosition, 1.958 + double baseValue, 1.959 + double upperBound, 1.960 + UBool lenientParse, 1.961 + Formattable& result) const 1.962 +{ 1.963 + // if this isn't a >>> substitution, we can just use the 1.964 + // inherited parse() routine to do the parsing 1.965 + if (ruleToUse == NULL) { 1.966 + return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result); 1.967 + 1.968 + // but if it IS a >>> substitution, we have to do it here: we 1.969 + // use the specific rule's doParse() method, and then we have to 1.970 + // do some of the other work of NFRuleSet.parse() 1.971 + } else { 1.972 + ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result); 1.973 + 1.974 + if (parsePosition.getIndex() != 0) { 1.975 + UErrorCode status = U_ZERO_ERROR; 1.976 + double tempResult = result.getDouble(status); 1.977 + tempResult = composeRuleValue(tempResult, baseValue); 1.978 + result.setDouble(tempResult); 1.979 + } 1.980 + 1.981 + return TRUE; 1.982 + } 1.983 +} 1.984 +/** 1.985 + * Returns a textual description of the substitution 1.986 + * @return A textual description of the substitution. This might 1.987 + * not be identical to the description it was created from, but 1.988 + * it'll produce the same result. 1.989 + */ 1.990 +void 1.991 +ModulusSubstitution::toString(UnicodeString& text) const 1.992 +{ 1.993 + // use tokenChar() to get the character at the beginning and 1.994 + // end of the substitutin token. In between them will go 1.995 + // either the name of the rule set it uses, or the pattern of 1.996 + // the DecimalFormat it uses 1.997 + 1.998 + if ( ruleToUse != NULL ) { // Must have been a >>> substitution. 1.999 + text.remove(); 1.1000 + text.append(tokenChar()); 1.1001 + text.append(tokenChar()); 1.1002 + text.append(tokenChar()); 1.1003 + } else { // Otherwise just use the super-class function. 1.1004 + NFSubstitution::toString(text); 1.1005 + } 1.1006 +} 1.1007 +//=================================================================== 1.1008 +// IntegralPartSubstitution 1.1009 +//=================================================================== 1.1010 + 1.1011 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) 1.1012 + 1.1013 + 1.1014 +//=================================================================== 1.1015 +// FractionalPartSubstitution 1.1016 +//=================================================================== 1.1017 + 1.1018 + 1.1019 + /** 1.1020 + * Constructs a FractionalPartSubstitution. This object keeps a flag 1.1021 + * telling whether it should format by digits or not. In addition, 1.1022 + * it marks the rule set it calls (if any) as a fraction rule set. 1.1023 + */ 1.1024 +FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, 1.1025 + const NFRuleSet* _ruleSet, 1.1026 + const RuleBasedNumberFormat* formatter, 1.1027 + const UnicodeString& description, 1.1028 + UErrorCode& status) 1.1029 + : NFSubstitution(_pos, _ruleSet, formatter, description, status) 1.1030 + , byDigits(FALSE) 1.1031 + , useSpaces(TRUE) 1.1032 + 1.1033 +{ 1.1034 + // akk, ruleSet can change in superclass constructor 1.1035 + if (0 == description.compare(gGreaterGreaterThan, 2) || 1.1036 + 0 == description.compare(gGreaterGreaterGreaterThan, 3) || 1.1037 + _ruleSet == getRuleSet()) { 1.1038 + byDigits = TRUE; 1.1039 + if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { 1.1040 + useSpaces = FALSE; 1.1041 + } 1.1042 + } else { 1.1043 + // cast away const 1.1044 + ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); 1.1045 + } 1.1046 +} 1.1047 + 1.1048 +//----------------------------------------------------------------------- 1.1049 +// formatting 1.1050 +//----------------------------------------------------------------------- 1.1051 + 1.1052 +/** 1.1053 + * If in "by digits" mode, fills in the substitution one decimal digit 1.1054 + * at a time using the rule set containing this substitution. 1.1055 + * Otherwise, uses the superclass function. 1.1056 + * @param number The number being formatted 1.1057 + * @param toInsertInto The string to insert the result of formatting 1.1058 + * the substitution into 1.1059 + * @param pos The position of the owning rule's rule text in 1.1060 + * toInsertInto 1.1061 + */ 1.1062 +void 1.1063 +FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const 1.1064 +{ 1.1065 + // if we're not in "byDigits" mode, just use the inherited 1.1066 + // doSubstitution() routine 1.1067 + if (!byDigits) { 1.1068 + NFSubstitution::doSubstitution(number, toInsertInto, _pos); 1.1069 + 1.1070 + // if we're in "byDigits" mode, transform the value into an integer 1.1071 + // by moving the decimal point eight places to the right and 1.1072 + // pulling digits off the right one at a time, formatting each digit 1.1073 + // as an integer using this substitution's owning rule set 1.1074 + // (this is slower, but more accurate, than doing it from the 1.1075 + // other end) 1.1076 + } else { 1.1077 + // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); 1.1078 + // // this flag keeps us from formatting trailing zeros. It starts 1.1079 + // // out false because we're pulling from the right, and switches 1.1080 + // // to true the first time we encounter a non-zero digit 1.1081 + // UBool doZeros = FALSE; 1.1082 + // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { 1.1083 + // int64_t digit = numberToFormat % 10; 1.1084 + // if (digit != 0 || doZeros) { 1.1085 + // if (doZeros && useSpaces) { 1.1086 + // toInsertInto.insert(_pos + getPos(), gSpace); 1.1087 + // } 1.1088 + // doZeros = TRUE; 1.1089 + // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); 1.1090 + // } 1.1091 + // numberToFormat /= 10; 1.1092 + // } 1.1093 + 1.1094 + DigitList dl; 1.1095 + dl.set(number); 1.1096 + dl.roundFixedPoint(20); // round to 20 fraction digits. 1.1097 + dl.reduce(); // Removes any trailing zeros. 1.1098 + 1.1099 + UBool pad = FALSE; 1.1100 + for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { 1.1101 + // Loop iterates over fraction digits, starting with the LSD. 1.1102 + // include both real digits from the number, and zeros 1.1103 + // to the left of the MSD but to the right of the decimal point. 1.1104 + if (pad && useSpaces) { 1.1105 + toInsertInto.insert(_pos + getPos(), gSpace); 1.1106 + } else { 1.1107 + pad = TRUE; 1.1108 + } 1.1109 + int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; 1.1110 + getRuleSet()->format(digit, toInsertInto, _pos + getPos()); 1.1111 + } 1.1112 + 1.1113 + if (!pad) { 1.1114 + // hack around lack of precision in digitlist. if we would end up with 1.1115 + // "foo point" make sure we add a " zero" to the end. 1.1116 + getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); 1.1117 + } 1.1118 + } 1.1119 +} 1.1120 + 1.1121 +//----------------------------------------------------------------------- 1.1122 +// parsing 1.1123 +//----------------------------------------------------------------------- 1.1124 + 1.1125 +/** 1.1126 + * If in "by digits" mode, parses the string as if it were a string 1.1127 + * of individual digits; otherwise, uses the superclass function. 1.1128 + * @param text The string to parse 1.1129 + * @param parsePosition Ignored on entry, but updated on exit to point 1.1130 + * to the first unmatched character 1.1131 + * @param baseValue The partial parse result prior to entering this 1.1132 + * function 1.1133 + * @param upperBound Only consider rules with base values lower than 1.1134 + * this when filling in the substitution 1.1135 + * @param lenientParse If true, try matching the text as numerals if 1.1136 + * matching as words doesn't work 1.1137 + * @return If the match was successful, the current partial parse 1.1138 + * result; otherwise new Long(0). The result is either a Long or 1.1139 + * a Double. 1.1140 + */ 1.1141 + 1.1142 +UBool 1.1143 +FractionalPartSubstitution::doParse(const UnicodeString& text, 1.1144 + ParsePosition& parsePosition, 1.1145 + double baseValue, 1.1146 + double /*upperBound*/, 1.1147 + UBool lenientParse, 1.1148 + Formattable& resVal) const 1.1149 +{ 1.1150 + // if we're not in byDigits mode, we can just use the inherited 1.1151 + // doParse() 1.1152 + if (!byDigits) { 1.1153 + return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); 1.1154 + 1.1155 + // if we ARE in byDigits mode, parse the text one digit at a time 1.1156 + // using this substitution's owning rule set (we do this by setting 1.1157 + // upperBound to 10 when calling doParse() ) until we reach 1.1158 + // nonmatching text 1.1159 + } else { 1.1160 + UnicodeString workText(text); 1.1161 + ParsePosition workPos(1); 1.1162 + double result = 0; 1.1163 + int32_t digit; 1.1164 +// double p10 = 0.1; 1.1165 + 1.1166 + DigitList dl; 1.1167 + NumberFormat* fmt = NULL; 1.1168 + while (workText.length() > 0 && workPos.getIndex() != 0) { 1.1169 + workPos.setIndex(0); 1.1170 + Formattable temp; 1.1171 + getRuleSet()->parse(workText, workPos, 10, temp); 1.1172 + UErrorCode status = U_ZERO_ERROR; 1.1173 + digit = temp.getLong(status); 1.1174 +// digit = temp.getType() == Formattable::kLong ? 1.1175 +// temp.getLong() : 1.1176 +// (int32_t)temp.getDouble(); 1.1177 + 1.1178 + if (lenientParse && workPos.getIndex() == 0) { 1.1179 + if (!fmt) { 1.1180 + status = U_ZERO_ERROR; 1.1181 + fmt = NumberFormat::createInstance(status); 1.1182 + if (U_FAILURE(status)) { 1.1183 + delete fmt; 1.1184 + fmt = NULL; 1.1185 + } 1.1186 + } 1.1187 + if (fmt) { 1.1188 + fmt->parse(workText, temp, workPos); 1.1189 + digit = temp.getLong(status); 1.1190 + } 1.1191 + } 1.1192 + 1.1193 + if (workPos.getIndex() != 0) { 1.1194 + dl.append((char)('0' + digit)); 1.1195 +// result += digit * p10; 1.1196 +// p10 /= 10; 1.1197 + parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1.1198 + workText.removeBetween(0, workPos.getIndex()); 1.1199 + while (workText.length() > 0 && workText.charAt(0) == gSpace) { 1.1200 + workText.removeBetween(0, 1); 1.1201 + parsePosition.setIndex(parsePosition.getIndex() + 1); 1.1202 + } 1.1203 + } 1.1204 + } 1.1205 + delete fmt; 1.1206 + 1.1207 + result = dl.getCount() == 0 ? 0 : dl.getDouble(); 1.1208 + result = composeRuleValue(result, baseValue); 1.1209 + resVal.setDouble(result); 1.1210 + return TRUE; 1.1211 + } 1.1212 +} 1.1213 + 1.1214 +UBool 1.1215 +FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const 1.1216 +{ 1.1217 + return NFSubstitution::operator==(rhs) && 1.1218 + ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; 1.1219 +} 1.1220 + 1.1221 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) 1.1222 + 1.1223 + 1.1224 +//=================================================================== 1.1225 +// AbsoluteValueSubstitution 1.1226 +//=================================================================== 1.1227 + 1.1228 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) 1.1229 + 1.1230 +//=================================================================== 1.1231 +// NumeratorSubstitution 1.1232 +//=================================================================== 1.1233 + 1.1234 +void 1.1235 +NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const { 1.1236 + // perform a transformation on the number being formatted that 1.1237 + // is dependent on the type of substitution this is 1.1238 + 1.1239 + double numberToFormat = transformNumber(number); 1.1240 + int64_t longNF = util64_fromDouble(numberToFormat); 1.1241 + 1.1242 + const NFRuleSet* aruleSet = getRuleSet(); 1.1243 + if (withZeros && aruleSet != NULL) { 1.1244 + // if there are leading zeros in the decimal expansion then emit them 1.1245 + int64_t nf =longNF; 1.1246 + int32_t len = toInsertInto.length(); 1.1247 + while ((nf *= 10) < denominator) { 1.1248 + toInsertInto.insert(apos + getPos(), gSpace); 1.1249 + aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); 1.1250 + } 1.1251 + apos += toInsertInto.length() - len; 1.1252 + } 1.1253 + 1.1254 + // if the result is an integer, from here on out we work in integer 1.1255 + // space (saving time and memory and preserving accuracy) 1.1256 + if (numberToFormat == longNF && aruleSet != NULL) { 1.1257 + aruleSet->format(longNF, toInsertInto, apos + getPos()); 1.1258 + 1.1259 + // if the result isn't an integer, then call either our rule set's 1.1260 + // format() method or our DecimalFormat's format() method to 1.1261 + // format the result 1.1262 + } else { 1.1263 + if (aruleSet != NULL) { 1.1264 + aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); 1.1265 + } else { 1.1266 + UErrorCode status = U_ZERO_ERROR; 1.1267 + UnicodeString temp; 1.1268 + getNumberFormat()->format(numberToFormat, temp, status); 1.1269 + toInsertInto.insert(apos + getPos(), temp); 1.1270 + } 1.1271 + } 1.1272 +} 1.1273 + 1.1274 +UBool 1.1275 +NumeratorSubstitution::doParse(const UnicodeString& text, 1.1276 + ParsePosition& parsePosition, 1.1277 + double baseValue, 1.1278 + double upperBound, 1.1279 + UBool /*lenientParse*/, 1.1280 + Formattable& result) const 1.1281 +{ 1.1282 + // we don't have to do anything special to do the parsing here, 1.1283 + // but we have to turn lenient parsing off-- if we leave it on, 1.1284 + // it SERIOUSLY messes up the algorithm 1.1285 + 1.1286 + // if withZeros is true, we need to count the zeros 1.1287 + // and use that to adjust the parse result 1.1288 + UErrorCode status = U_ZERO_ERROR; 1.1289 + int32_t zeroCount = 0; 1.1290 + UnicodeString workText(text); 1.1291 + 1.1292 + if (withZeros) { 1.1293 + ParsePosition workPos(1); 1.1294 + Formattable temp; 1.1295 + 1.1296 + while (workText.length() > 0 && workPos.getIndex() != 0) { 1.1297 + workPos.setIndex(0); 1.1298 + getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all 1.1299 + if (workPos.getIndex() == 0) { 1.1300 + // we failed, either there were no more zeros, or the number was formatted with digits 1.1301 + // either way, we're done 1.1302 + break; 1.1303 + } 1.1304 + 1.1305 + ++zeroCount; 1.1306 + parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); 1.1307 + workText.remove(0, workPos.getIndex()); 1.1308 + while (workText.length() > 0 && workText.charAt(0) == gSpace) { 1.1309 + workText.remove(0, 1); 1.1310 + parsePosition.setIndex(parsePosition.getIndex() + 1); 1.1311 + } 1.1312 + } 1.1313 + 1.1314 + workText = text; 1.1315 + workText.remove(0, (int32_t)parsePosition.getIndex()); 1.1316 + parsePosition.setIndex(0); 1.1317 + } 1.1318 + 1.1319 + // we've parsed off the zeros, now let's parse the rest from our current position 1.1320 + NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result); 1.1321 + 1.1322 + if (withZeros) { 1.1323 + // any base value will do in this case. is there a way to 1.1324 + // force this to not bother trying all the base values? 1.1325 + 1.1326 + // compute the 'effective' base and prescale the value down 1.1327 + int64_t n = result.getLong(status); // force conversion! 1.1328 + int64_t d = 1; 1.1329 + int32_t pow = 0; 1.1330 + while (d <= n) { 1.1331 + d *= 10; 1.1332 + ++pow; 1.1333 + } 1.1334 + // now add the zeros 1.1335 + while (zeroCount > 0) { 1.1336 + d *= 10; 1.1337 + --zeroCount; 1.1338 + } 1.1339 + // d is now our true denominator 1.1340 + result.setDouble((double)n/(double)d); 1.1341 + } 1.1342 + 1.1343 + return TRUE; 1.1344 +} 1.1345 + 1.1346 +UBool 1.1347 +NumeratorSubstitution::operator==(const NFSubstitution& rhs) const 1.1348 +{ 1.1349 + return NFSubstitution::operator==(rhs) && 1.1350 + denominator == ((const NumeratorSubstitution*)&rhs)->denominator; 1.1351 +} 1.1352 + 1.1353 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) 1.1354 + 1.1355 +const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; 1.1356 + 1.1357 +//=================================================================== 1.1358 +// NullSubstitution 1.1359 +//=================================================================== 1.1360 + 1.1361 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution) 1.1362 + 1.1363 +U_NAMESPACE_END 1.1364 + 1.1365 +/* U_HAVE_RBNF */ 1.1366 +#endif 1.1367 +