OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ****************************************************************************** |
| 3 * Copyright (C) 1997-2010, International Business Machines |
| 4 * Corporation and others. All Rights Reserved. |
| 5 ****************************************************************************** |
| 6 * file name: nfsubs.cpp |
| 7 * encoding: US-ASCII |
| 8 * tab size: 8 (not used) |
| 9 * indentation:4 |
| 10 * |
| 11 * Modification history |
| 12 * Date Name Comments |
| 13 * 10/11/2001 Doug Ported from ICU4J |
| 14 */ |
| 15 |
| 16 #include <stdio.h> |
| 17 #include <typeinfo> // for 'typeid' to work |
| 18 |
| 19 #include "nfsubs.h" |
| 20 #include "digitlst.h" |
| 21 |
| 22 #if U_HAVE_RBNF |
| 23 |
| 24 static const UChar gLessThan = 0x003c; |
| 25 static const UChar gEquals = 0x003d; |
| 26 static const UChar gGreaterThan = 0x003e; |
| 27 static const UChar gPercent = 0x0025; |
| 28 static const UChar gPound = 0x0023; |
| 29 static const UChar gZero = 0x0030; |
| 30 static const UChar gSpace = 0x0020; |
| 31 |
| 32 static const UChar gEqualsEquals[] = |
| 33 { |
| 34 0x3D, 0x3D, 0 |
| 35 }; /* "==" */ |
| 36 static const UChar gGreaterGreaterGreaterThan[] = |
| 37 { |
| 38 0x3E, 0x3E, 0x3E, 0 |
| 39 }; /* ">>>" */ |
| 40 static const UChar gGreaterGreaterThan[] = |
| 41 { |
| 42 0x3E, 0x3E, 0 |
| 43 }; /* ">>" */ |
| 44 |
| 45 U_NAMESPACE_BEGIN |
| 46 |
| 47 class SameValueSubstitution : public NFSubstitution { |
| 48 public: |
| 49 SameValueSubstitution(int32_t pos, |
| 50 const NFRuleSet* ruleset, |
| 51 const RuleBasedNumberFormat* formatter, |
| 52 const UnicodeString& description, |
| 53 UErrorCode& status); |
| 54 |
| 55 virtual int64_t transformNumber(int64_t number) const { return number; } |
| 56 virtual double transformNumber(double number) const { return number; } |
| 57 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/
) const { return newRuleValue; } |
| 58 virtual double calcUpperBound(double oldUpperBound) const { return oldUpperB
ound; } |
| 59 virtual UChar tokenChar() const { return (UChar)0x003d; } // '=' |
| 60 |
| 61 public: |
| 62 static UClassID getStaticClassID(void); |
| 63 virtual UClassID getDynamicClassID(void) const; |
| 64 }; |
| 65 |
| 66 class MultiplierSubstitution : public NFSubstitution { |
| 67 double divisor; |
| 68 int64_t ldivisor; |
| 69 |
| 70 public: |
| 71 MultiplierSubstitution(int32_t _pos, |
| 72 double _divisor, |
| 73 const NFRuleSet* _ruleSet, |
| 74 const RuleBasedNumberFormat* formatter, |
| 75 const UnicodeString& description, |
| 76 UErrorCode& status) |
| 77 : NFSubstitution(_pos, _ruleSet, formatter, description, status), diviso
r(_divisor) |
| 78 { |
| 79 ldivisor = util64_fromDouble(divisor); |
| 80 if (divisor == 0) { |
| 81 status = U_PARSE_ERROR; |
| 82 } |
| 83 } |
| 84 |
| 85 virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status)
{ |
| 86 divisor = uprv_pow(radix, exponent); |
| 87 ldivisor = util64_fromDouble(divisor); |
| 88 |
| 89 if(divisor == 0) { |
| 90 status = U_PARSE_ERROR; |
| 91 } |
| 92 } |
| 93 |
| 94 virtual UBool operator==(const NFSubstitution& rhs) const; |
| 95 |
| 96 virtual int64_t transformNumber(int64_t number) const { |
| 97 return number / ldivisor; |
| 98 } |
| 99 |
| 100 virtual double transformNumber(double number) const { |
| 101 if (getRuleSet()) { |
| 102 return uprv_floor(number / divisor); |
| 103 } else { |
| 104 return number/divisor; |
| 105 } |
| 106 } |
| 107 |
| 108 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/
) const { |
| 109 return newRuleValue * divisor; |
| 110 } |
| 111 |
| 112 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divis
or; } |
| 113 |
| 114 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' |
| 115 |
| 116 public: |
| 117 static UClassID getStaticClassID(void); |
| 118 virtual UClassID getDynamicClassID(void) const; |
| 119 }; |
| 120 |
| 121 class ModulusSubstitution : public NFSubstitution { |
| 122 double divisor; |
| 123 int64_t ldivisor; |
| 124 const NFRule* ruleToUse; |
| 125 public: |
| 126 ModulusSubstitution(int32_t pos, |
| 127 double _divisor, |
| 128 const NFRule* rulePredecessor, |
| 129 const NFRuleSet* ruleSet, |
| 130 const RuleBasedNumberFormat* formatter, |
| 131 const UnicodeString& description, |
| 132 UErrorCode& status); |
| 133 |
| 134 virtual void setDivisor(int32_t radix, int32_t exponent, UErrorCode& status)
{ |
| 135 divisor = uprv_pow(radix, exponent); |
| 136 ldivisor = util64_fromDouble(divisor); |
| 137 |
| 138 if (divisor == 0) { |
| 139 status = U_PARSE_ERROR; |
| 140 } |
| 141 } |
| 142 |
| 143 virtual UBool operator==(const NFSubstitution& rhs) const; |
| 144 |
| 145 virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int
32_t pos) const; |
| 146 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int3
2_t pos) const; |
| 147 |
| 148 virtual int64_t transformNumber(int64_t number) const { return number % ldiv
isor; } |
| 149 virtual double transformNumber(double number) const { return uprv_fmod(numbe
r, divisor); } |
| 150 |
| 151 virtual UBool doParse(const UnicodeString& text, |
| 152 ParsePosition& parsePosition, |
| 153 double baseValue, |
| 154 double upperBound, |
| 155 UBool lenientParse, |
| 156 Formattable& result) const; |
| 157 |
| 158 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) co
nst { |
| 159 return oldRuleValue - uprv_fmod(oldRuleValue, divisor) + newRuleValue; |
| 160 } |
| 161 |
| 162 virtual double calcUpperBound(double /*oldUpperBound*/) const { return divis
or; } |
| 163 |
| 164 virtual UBool isModulusSubstitution() const { return TRUE; } |
| 165 |
| 166 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' |
| 167 |
| 168 public: |
| 169 static UClassID getStaticClassID(void); |
| 170 virtual UClassID getDynamicClassID(void) const; |
| 171 }; |
| 172 |
| 173 class IntegralPartSubstitution : public NFSubstitution { |
| 174 public: |
| 175 IntegralPartSubstitution(int32_t _pos, |
| 176 const NFRuleSet* _ruleSet, |
| 177 const RuleBasedNumberFormat* formatter, |
| 178 const UnicodeString& description, |
| 179 UErrorCode& status) |
| 180 : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} |
| 181 |
| 182 virtual int64_t transformNumber(int64_t number) const { return number; } |
| 183 virtual double transformNumber(double number) const { return uprv_floor(numb
er); } |
| 184 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) co
nst { return newRuleValue + oldRuleValue; } |
| 185 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_M
AX; } |
| 186 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' |
| 187 |
| 188 public: |
| 189 static UClassID getStaticClassID(void); |
| 190 virtual UClassID getDynamicClassID(void) const; |
| 191 }; |
| 192 |
| 193 class FractionalPartSubstitution : public NFSubstitution { |
| 194 UBool byDigits; |
| 195 UBool useSpaces; |
| 196 enum { kMaxDecimalDigits = 8 }; |
| 197 public: |
| 198 FractionalPartSubstitution(int32_t pos, |
| 199 const NFRuleSet* ruleSet, |
| 200 const RuleBasedNumberFormat* formatter, |
| 201 const UnicodeString& description, |
| 202 UErrorCode& status); |
| 203 |
| 204 virtual UBool operator==(const NFSubstitution& rhs) const; |
| 205 |
| 206 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int3
2_t pos) const; |
| 207 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInt
o*/, int32_t /*_pos*/) const {} |
| 208 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } |
| 209 virtual double transformNumber(double number) const { return number - uprv_f
loor(number); } |
| 210 |
| 211 virtual UBool doParse(const UnicodeString& text, |
| 212 ParsePosition& parsePosition, |
| 213 double baseValue, |
| 214 double upperBound, |
| 215 UBool lenientParse, |
| 216 Formattable& result) const; |
| 217 |
| 218 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) co
nst { return newRuleValue + oldRuleValue; } |
| 219 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0.0;
} |
| 220 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' |
| 221 |
| 222 public: |
| 223 static UClassID getStaticClassID(void); |
| 224 virtual UClassID getDynamicClassID(void) const; |
| 225 }; |
| 226 |
| 227 class AbsoluteValueSubstitution : public NFSubstitution { |
| 228 public: |
| 229 AbsoluteValueSubstitution(int32_t _pos, |
| 230 const NFRuleSet* _ruleSet, |
| 231 const RuleBasedNumberFormat* formatter, |
| 232 const UnicodeString& description, |
| 233 UErrorCode& status) |
| 234 : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} |
| 235 |
| 236 virtual int64_t transformNumber(int64_t number) const { return number >= 0 ?
number : -number; } |
| 237 virtual double transformNumber(double number) const { return uprv_fabs(numbe
r); } |
| 238 virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/
) const { return -newRuleValue; } |
| 239 virtual double calcUpperBound(double /*oldUpperBound*/) const { return DBL_M
AX; } |
| 240 virtual UChar tokenChar() const { return (UChar)0x003e; } // '>' |
| 241 |
| 242 public: |
| 243 static UClassID getStaticClassID(void); |
| 244 virtual UClassID getDynamicClassID(void) const; |
| 245 }; |
| 246 |
| 247 class NumeratorSubstitution : public NFSubstitution { |
| 248 double denominator; |
| 249 int64_t ldenominator; |
| 250 UBool withZeros; |
| 251 public: |
| 252 static inline UnicodeString fixdesc(const UnicodeString& desc) { |
| 253 if (desc.endsWith(LTLT, 2)) { |
| 254 UnicodeString result(desc, 0, desc.length()-1); |
| 255 return result; |
| 256 } |
| 257 return desc; |
| 258 } |
| 259 NumeratorSubstitution(int32_t _pos, |
| 260 double _denominator, |
| 261 const NFRuleSet* _ruleSet, |
| 262 const RuleBasedNumberFormat* formatter, |
| 263 const UnicodeString& description, |
| 264 UErrorCode& status) |
| 265 : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status
), denominator(_denominator) |
| 266 { |
| 267 ldenominator = util64_fromDouble(denominator); |
| 268 withZeros = description.endsWith(LTLT, 2); |
| 269 } |
| 270 |
| 271 virtual UBool operator==(const NFSubstitution& rhs) const; |
| 272 |
| 273 virtual int64_t transformNumber(int64_t number) const { return number * lden
ominator; } |
| 274 virtual double transformNumber(double number) const { return uprv_round(numb
er * denominator); } |
| 275 |
| 276 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInt
o*/, int32_t /*_pos*/) const {} |
| 277 virtual void doSubstitution(double number, UnicodeString& toInsertInto, int3
2_t pos) const; |
| 278 virtual UBool doParse(const UnicodeString& text, |
| 279 ParsePosition& parsePosition, |
| 280 double baseValue, |
| 281 double upperBound, |
| 282 UBool /*lenientParse*/, |
| 283 Formattable& result) const; |
| 284 |
| 285 virtual double composeRuleValue(double newRuleValue, double oldRuleValue) co
nst { return newRuleValue / oldRuleValue; } |
| 286 virtual double calcUpperBound(double /*oldUpperBound*/) const { return denom
inator; } |
| 287 virtual UChar tokenChar() const { return (UChar)0x003c; } // '<' |
| 288 private: |
| 289 static const UChar LTLT[2]; |
| 290 |
| 291 public: |
| 292 static UClassID getStaticClassID(void); |
| 293 virtual UClassID getDynamicClassID(void) const; |
| 294 }; |
| 295 |
| 296 class NullSubstitution : public NFSubstitution { |
| 297 public: |
| 298 NullSubstitution(int32_t _pos, |
| 299 const NFRuleSet* _ruleSet, |
| 300 const RuleBasedNumberFormat* formatter, |
| 301 const UnicodeString& description, |
| 302 UErrorCode& status) |
| 303 : NFSubstitution(_pos, _ruleSet, formatter, description, status) {} |
| 304 |
| 305 virtual void toString(UnicodeString& /*result*/) const {} |
| 306 virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto
*/, int32_t /*_pos*/) const {} |
| 307 virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInt
o*/, int32_t /*_pos*/) const {} |
| 308 virtual int64_t transformNumber(int64_t /*number*/) const { return 0; } |
| 309 virtual double transformNumber(double /*number*/) const { return 0; } |
| 310 virtual UBool doParse(const UnicodeString& /*text*/, |
| 311 ParsePosition& /*parsePosition*/, |
| 312 double baseValue, |
| 313 double /*upperBound*/, |
| 314 UBool /*lenientParse*/, |
| 315 Formattable& result) const |
| 316 { result.setDouble(baseValue); return TRUE; } |
| 317 virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleVal
ue*/) const { return 0.0; } // never called |
| 318 virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; }
// never called |
| 319 virtual UBool isNullSubstitution() const { return TRUE; } |
| 320 virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never calle
d |
| 321 |
| 322 public: |
| 323 static UClassID getStaticClassID(void); |
| 324 virtual UClassID getDynamicClassID(void) const; |
| 325 }; |
| 326 |
| 327 NFSubstitution* |
| 328 NFSubstitution::makeSubstitution(int32_t pos, |
| 329 const NFRule* rule, |
| 330 const NFRule* predecessor, |
| 331 const NFRuleSet* ruleSet, |
| 332 const RuleBasedNumberFormat* formatter, |
| 333 const UnicodeString& description, |
| 334 UErrorCode& status) |
| 335 { |
| 336 // if the description is empty, return a NullSubstitution |
| 337 if (description.length() == 0) { |
| 338 return new NullSubstitution(pos, ruleSet, formatter, description, status
); |
| 339 } |
| 340 |
| 341 switch (description.charAt(0)) { |
| 342 // if the description begins with '<'... |
| 343 case gLessThan: |
| 344 // throw an exception if the rule is a negative number |
| 345 // rule |
| 346 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { |
| 347 // throw new IllegalArgumentException("<< not allowed in negative-nu
mber rule"); |
| 348 status = U_PARSE_ERROR; |
| 349 return NULL; |
| 350 } |
| 351 |
| 352 // if the rule is a fraction rule, return an |
| 353 // IntegralPartSubstitution |
| 354 else if (rule->getBaseValue() == NFRule::kImproperFractionRule |
| 355 || rule->getBaseValue() == NFRule::kProperFractionRule |
| 356 || rule->getBaseValue() == NFRule::kMasterRule) { |
| 357 return new IntegralPartSubstitution(pos, ruleSet, formatter, descrip
tion, status); |
| 358 } |
| 359 |
| 360 // if the rule set containing the rule is a fraction |
| 361 // rule set, return a NumeratorSubstitution |
| 362 else if (ruleSet->isFractionRuleSet()) { |
| 363 return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), |
| 364 formatter->getDefaultRuleSet(), formatter, description, status); |
| 365 } |
| 366 |
| 367 // otherwise, return a MultiplierSubstitution |
| 368 else { |
| 369 return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet, |
| 370 formatter, description, status); |
| 371 } |
| 372 |
| 373 // if the description begins with '>'... |
| 374 case gGreaterThan: |
| 375 // if the rule is a negative-number rule, return |
| 376 // an AbsoluteValueSubstitution |
| 377 if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { |
| 378 return new AbsoluteValueSubstitution(pos, ruleSet, formatter, descri
ption, status); |
| 379 } |
| 380 |
| 381 // if the rule is a fraction rule, return a |
| 382 // FractionalPartSubstitution |
| 383 else if (rule->getBaseValue() == NFRule::kImproperFractionRule |
| 384 || rule->getBaseValue() == NFRule::kProperFractionRule |
| 385 || rule->getBaseValue() == NFRule::kMasterRule) { |
| 386 return new FractionalPartSubstitution(pos, ruleSet, formatter, descr
iption, status); |
| 387 } |
| 388 |
| 389 // if the rule set owning the rule is a fraction rule set, |
| 390 // throw an exception |
| 391 else if (ruleSet->isFractionRuleSet()) { |
| 392 // throw new IllegalArgumentException(">> not allowed in fraction ru
le set"); |
| 393 status = U_PARSE_ERROR; |
| 394 return NULL; |
| 395 } |
| 396 |
| 397 // otherwise, return a ModulusSubstitution |
| 398 else { |
| 399 return new ModulusSubstitution(pos, rule->getDivisor(), predecessor, |
| 400 ruleSet, formatter, description, status); |
| 401 } |
| 402 |
| 403 // if the description begins with '=', always return a |
| 404 // SameValueSubstitution |
| 405 case gEquals: |
| 406 return new SameValueSubstitution(pos, ruleSet, formatter, description, s
tatus); |
| 407 |
| 408 // and if it's anything else, throw an exception |
| 409 default: |
| 410 // throw new IllegalArgumentException("Illegal substitution character"); |
| 411 status = U_PARSE_ERROR; |
| 412 } |
| 413 return NULL; |
| 414 } |
| 415 |
| 416 NFSubstitution::NFSubstitution(int32_t _pos, |
| 417 const NFRuleSet* _ruleSet, |
| 418 const RuleBasedNumberFormat* formatter, |
| 419 const UnicodeString& description, |
| 420 UErrorCode& status) |
| 421 : pos(_pos), ruleSet(NULL), numberFormat(NULL) |
| 422 { |
| 423 // the description should begin and end with the same character. |
| 424 // If it doesn't that's a syntax error. Otherwise, |
| 425 // makeSubstitution() was the only thing that needed to know |
| 426 // about these characters, so strip them off |
| 427 UnicodeString workingDescription(description); |
| 428 if (description.length() >= 2 |
| 429 && description.charAt(0) == description.charAt(description.length() - 1)
) |
| 430 { |
| 431 workingDescription.remove(description.length() - 1, 1); |
| 432 workingDescription.remove(0, 1); |
| 433 } |
| 434 else if (description.length() != 0) { |
| 435 // throw new IllegalArgumentException("Illegal substitution syntax"); |
| 436 status = U_PARSE_ERROR; |
| 437 return; |
| 438 } |
| 439 |
| 440 // if the description was just two paired token characters |
| 441 // (i.e., "<<" or ">>"), it uses the rule set it belongs to to |
| 442 // format its result |
| 443 if (workingDescription.length() == 0) { |
| 444 this->ruleSet = _ruleSet; |
| 445 } |
| 446 // if the description contains a rule set name, that's the rule |
| 447 // set we use to format the result: get a reference to the |
| 448 // names rule set |
| 449 else if (workingDescription.charAt(0) == gPercent) { |
| 450 this->ruleSet = formatter->findRuleSet(workingDescription, status); |
| 451 } |
| 452 // if the description begins with 0 or #, treat it as a |
| 453 // DecimalFormat pattern, and initialize a DecimalFormat with |
| 454 // that pattern (then set it to use the DecimalFormatSymbols |
| 455 // belonging to our formatter) |
| 456 else if (workingDescription.charAt(0) == gPound || workingDescription.charAt
(0) ==gZero) { |
| 457 DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols(); |
| 458 if (!sym) { |
| 459 status = U_MISSING_RESOURCE_ERROR; |
| 460 return; |
| 461 } |
| 462 this->numberFormat = new DecimalFormat(workingDescription, *sym, status)
; |
| 463 /* test for NULL */ |
| 464 if (this->numberFormat == 0) { |
| 465 status = U_MEMORY_ALLOCATION_ERROR; |
| 466 return; |
| 467 } |
| 468 if (U_FAILURE(status)) { |
| 469 delete (DecimalFormat*)this->numberFormat; |
| 470 this->numberFormat = NULL; |
| 471 return; |
| 472 } |
| 473 // this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalForm
atSymbols()); |
| 474 } |
| 475 // if the description is ">>>", this substitution bypasses the |
| 476 // usual rule-search process and always uses the rule that precedes |
| 477 // it in its own rule set's rule list (this is used for place-value |
| 478 // notations: formats where you want to see a particular part of |
| 479 // a number even when it's 0) |
| 480 else if (workingDescription.charAt(0) == gGreaterThan) { |
| 481 // this causes problems when >>> is used in a frationalPartSubstitution |
| 482 // this->ruleSet = NULL; |
| 483 this->ruleSet = _ruleSet; |
| 484 this->numberFormat = NULL; |
| 485 } |
| 486 // and of the description is none of these things, it's a syntax error |
| 487 else { |
| 488 // throw new IllegalArgumentException("Illegal substitution syntax"); |
| 489 status = U_PARSE_ERROR; |
| 490 } |
| 491 } |
| 492 |
| 493 NFSubstitution::~NFSubstitution() |
| 494 { |
| 495 // cast away const |
| 496 delete (NumberFormat*)numberFormat; numberFormat = NULL; |
| 497 } |
| 498 |
| 499 /** |
| 500 * Set's the substitution's divisor. Used by NFRule.setBaseValue(). |
| 501 * A no-op for all substitutions except multiplier and modulus |
| 502 * substitutions. |
| 503 * @param radix The radix of the divisor |
| 504 * @param exponent The exponent of the divisor |
| 505 */ |
| 506 void |
| 507 NFSubstitution::setDivisor(int32_t /*radix*/, int32_t /*exponent*/, UErrorCode&
/*status*/) { |
| 508 // a no-op for all substitutions except multiplier and modulus substitutions |
| 509 } |
| 510 |
| 511 |
| 512 //----------------------------------------------------------------------- |
| 513 // boilerplate |
| 514 //----------------------------------------------------------------------- |
| 515 |
| 516 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) |
| 517 |
| 518 /** |
| 519 * Compares two substitutions for equality |
| 520 * @param The substitution to compare this one to |
| 521 * @return true if the two substitutions are functionally equivalent |
| 522 */ |
| 523 UBool |
| 524 NFSubstitution::operator==(const NFSubstitution& rhs) const |
| 525 { |
| 526 // compare class and all of the fields all substitutions have |
| 527 // in common |
| 528 // this should be called by subclasses before their own equality tests |
| 529 return typeid(*this) == typeid(rhs) |
| 530 && pos == rhs.pos |
| 531 && (ruleSet == NULL) == (rhs.ruleSet == NULL) |
| 532 // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? |
| 533 && (numberFormat == NULL |
| 534 ? (rhs.numberFormat == NULL) |
| 535 : (*numberFormat == *rhs.numberFormat)); |
| 536 } |
| 537 |
| 538 /** |
| 539 * Returns a textual description of the substitution |
| 540 * @return A textual description of the substitution. This might |
| 541 * not be identical to the description it was created from, but |
| 542 * it'll produce the same result. |
| 543 */ |
| 544 void |
| 545 NFSubstitution::toString(UnicodeString& text) const |
| 546 { |
| 547 // use tokenChar() to get the character at the beginning and |
| 548 // end of the substitutin token. In between them will go |
| 549 // either the name of the rule set it uses, or the pattern of |
| 550 // the DecimalFormat it uses |
| 551 text.remove(); |
| 552 text.append(tokenChar()); |
| 553 |
| 554 UnicodeString temp; |
| 555 if (ruleSet != NULL) { |
| 556 ruleSet->getName(temp); |
| 557 } else if (numberFormat != NULL) { |
| 558 numberFormat->toPattern(temp); |
| 559 } |
| 560 text.append(temp); |
| 561 text.append(tokenChar()); |
| 562 } |
| 563 |
| 564 //----------------------------------------------------------------------- |
| 565 // formatting |
| 566 //----------------------------------------------------------------------- |
| 567 |
| 568 /** |
| 569 * Performs a mathematical operation on the number, formats it using |
| 570 * either ruleSet or decimalFormat, and inserts the result into |
| 571 * toInsertInto. |
| 572 * @param number The number being formatted. |
| 573 * @param toInsertInto The string we insert the result into |
| 574 * @param pos The position in toInsertInto where the owning rule's |
| 575 * rule text begins (this value is added to this substitution's |
| 576 * position to determine exactly where to insert the new text) |
| 577 */ |
| 578 void |
| 579 NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int3
2_t _pos) const |
| 580 { |
| 581 if (ruleSet != NULL) { |
| 582 // perform a transformation on the number that is dependent |
| 583 // on the type of substitution this is, then just call its |
| 584 // rule set's format() method to format the result |
| 585 ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos)
; |
| 586 } else if (numberFormat != NULL) { |
| 587 // or perform the transformation on the number (preserving |
| 588 // the result's fractional part if the formatter it set |
| 589 // to show it), then use that formatter's format() method |
| 590 // to format the result |
| 591 double numberToFormat = transformNumber((double)number); |
| 592 if (numberFormat->getMaximumFractionDigits() == 0) { |
| 593 numberToFormat = uprv_floor(numberToFormat); |
| 594 } |
| 595 |
| 596 UnicodeString temp; |
| 597 numberFormat->format(numberToFormat, temp); |
| 598 toInsertInto.insert(_pos + this->pos, temp); |
| 599 } |
| 600 } |
| 601 |
| 602 /** |
| 603 * Performs a mathematical operation on the number, formats it using |
| 604 * either ruleSet or decimalFormat, and inserts the result into |
| 605 * toInsertInto. |
| 606 * @param number The number being formatted. |
| 607 * @param toInsertInto The string we insert the result into |
| 608 * @param pos The position in toInsertInto where the owning rule's |
| 609 * rule text begins (this value is added to this substitution's |
| 610 * position to determine exactly where to insert the new text) |
| 611 */ |
| 612 void |
| 613 NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32
_t _pos) const { |
| 614 // perform a transformation on the number being formatted that |
| 615 // is dependent on the type of substitution this is |
| 616 double numberToFormat = transformNumber(number); |
| 617 |
| 618 // if the result is an integer, from here on out we work in integer |
| 619 // space (saving time and memory and preserving accuracy) |
| 620 if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { |
| 621 ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos +
this->pos); |
| 622 |
| 623 // if the result isn't an integer, then call either our rule set's |
| 624 // format() method or our DecimalFormat's format() method to |
| 625 // format the result |
| 626 } else { |
| 627 if (ruleSet != NULL) { |
| 628 ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos); |
| 629 } else if (numberFormat != NULL) { |
| 630 UnicodeString temp; |
| 631 numberFormat->format(numberToFormat, temp); |
| 632 toInsertInto.insert(_pos + this->pos, temp); |
| 633 } |
| 634 } |
| 635 } |
| 636 |
| 637 |
| 638 //----------------------------------------------------------------------- |
| 639 // parsing |
| 640 //----------------------------------------------------------------------- |
| 641 |
| 642 #ifdef RBNF_DEBUG |
| 643 #include <stdio.h> |
| 644 #endif |
| 645 |
| 646 /** |
| 647 * Parses a string using the rule set or DecimalFormat belonging |
| 648 * to this substitution. If there's a match, a mathematical |
| 649 * operation (the inverse of the one used in formatting) is |
| 650 * performed on the result of the parse and the value passed in |
| 651 * and returned as the result. The parse position is updated to |
| 652 * point to the first unmatched character in the string. |
| 653 * @param text The string to parse |
| 654 * @param parsePosition On entry, ignored, but assumed to be 0. |
| 655 * On exit, this is updated to point to the first unmatched |
| 656 * character (or 0 if the substitution didn't match) |
| 657 * @param baseValue A partial parse result that should be |
| 658 * combined with the result of this parse |
| 659 * @param upperBound When searching the rule set for a rule |
| 660 * matching the string passed in, only rules with base values |
| 661 * lower than this are considered |
| 662 * @param lenientParse If true and matching against rules fails, |
| 663 * the substitution will also try matching the text against |
| 664 * numerals using a default-costructed NumberFormat. If false, |
| 665 * no extra work is done. (This value is false whenever the |
| 666 * formatter isn't in lenient-parse mode, but is also false |
| 667 * under some conditions even when the formatter _is_ in |
| 668 * lenient-parse mode.) |
| 669 * @return If there's a match, this is the result of composing |
| 670 * baseValue with whatever was returned from matching the |
| 671 * characters. This will be either a Long or a Double. If there's |
| 672 * no match this is new Long(0) (not null), and parsePosition |
| 673 * is left unchanged. |
| 674 */ |
| 675 UBool |
| 676 NFSubstitution::doParse(const UnicodeString& text, |
| 677 ParsePosition& parsePosition, |
| 678 double baseValue, |
| 679 double upperBound, |
| 680 UBool lenientParse, |
| 681 Formattable& result) const |
| 682 { |
| 683 #ifdef RBNF_DEBUG |
| 684 fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound); |
| 685 #endif |
| 686 // figure out the highest base value a rule can have and match |
| 687 // the text being parsed (this varies according to the type of |
| 688 // substitutions: multiplier, modulus, and numerator substitutions |
| 689 // restrict the search to rules with base values lower than their |
| 690 // own; same-value substitutions leave the upper bound wherever |
| 691 // it was, and the others allow any rule to match |
| 692 upperBound = calcUpperBound(upperBound); |
| 693 |
| 694 // use our rule set to parse the text. If that fails and |
| 695 // lenient parsing is enabled (this is always false if the |
| 696 // formatter's lenient-parsing mode is off, but it may also |
| 697 // be false even when the formatter's lenient-parse mode is |
| 698 // on), then also try parsing the text using a default- |
| 699 // constructed NumberFormat |
| 700 if (ruleSet != NULL) { |
| 701 ruleSet->parse(text, parsePosition, upperBound, result); |
| 702 if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIn
dex() == 0) { |
| 703 UErrorCode status = U_ZERO_ERROR; |
| 704 NumberFormat* fmt = NumberFormat::createInstance(status); |
| 705 if (U_SUCCESS(status)) { |
| 706 fmt->parse(text, result, parsePosition); |
| 707 } |
| 708 delete fmt; |
| 709 } |
| 710 |
| 711 // ...or use our DecimalFormat to parse the text |
| 712 } else if (numberFormat != NULL) { |
| 713 numberFormat->parse(text, result, parsePosition); |
| 714 } |
| 715 |
| 716 // if the parse was successful, we've already advanced the caller's |
| 717 // parse position (this is the one function that doesn't have one |
| 718 // of its own). Derive a parse result and return it as a Long, |
| 719 // if possible, or a Double |
| 720 if (parsePosition.getIndex() != 0) { |
| 721 UErrorCode status = U_ZERO_ERROR; |
| 722 double tempResult = result.getDouble(status); |
| 723 |
| 724 // composeRuleValue() produces a full parse result from |
| 725 // the partial parse result passed to this function from |
| 726 // the caller (this is either the owning rule's base value |
| 727 // or the partial result obtained from composing the |
| 728 // owning rule's base value with its other substitution's |
| 729 // parse result) and the partial parse result obtained by |
| 730 // matching the substitution (which will be the same value |
| 731 // the caller would get by parsing just this part of the |
| 732 // text with RuleBasedNumberFormat.parse() ). How the two |
| 733 // values are used to derive the full parse result depends |
| 734 // on the types of substitutions: For a regular rule, the |
| 735 // ultimate result is its multiplier substitution's result |
| 736 // times the rule's divisor (or the rule's base value) plus |
| 737 // the modulus substitution's result (which will actually |
| 738 // supersede part of the rule's base value). For a negative- |
| 739 // number rule, the result is the negative of its substitution's |
| 740 // result. For a fraction rule, it's the sum of its two |
| 741 // substitution results. For a rule in a fraction rule set, |
| 742 // it's the numerator substitution's result divided by |
| 743 // the rule's base value. Results from same-value substitutions |
| 744 // propagate back upard, and null substitutions don't affect |
| 745 // the result. |
| 746 tempResult = composeRuleValue(tempResult, baseValue); |
| 747 result.setDouble(tempResult); |
| 748 return TRUE; |
| 749 // if the parse was UNsuccessful, return 0 |
| 750 } else { |
| 751 result.setLong(0); |
| 752 return FALSE; |
| 753 } |
| 754 } |
| 755 |
| 756 UBool |
| 757 NFSubstitution::isNullSubstitution() const { |
| 758 return FALSE; |
| 759 } |
| 760 |
| 761 /** |
| 762 * Returns true if this is a modulus substitution. (We didn't do this |
| 763 * with instanceof partially because it causes source files to |
| 764 * proliferate and partially because we have to port this to C++.) |
| 765 * @return true if this object is an instance of ModulusSubstitution |
| 766 */ |
| 767 UBool |
| 768 NFSubstitution::isModulusSubstitution() const { |
| 769 return FALSE; |
| 770 } |
| 771 |
| 772 //=================================================================== |
| 773 // SameValueSubstitution |
| 774 //=================================================================== |
| 775 |
| 776 /** |
| 777 * A substitution that passes the value passed to it through unchanged. |
| 778 * Represented by == in rule descriptions. |
| 779 */ |
| 780 SameValueSubstitution::SameValueSubstitution(int32_t _pos, |
| 781 const NFRuleSet* _ruleSet, |
| 782 const RuleBasedNumberFormat* formatter, |
| 783 const UnicodeString& description, |
| 784 UErrorCode& status) |
| 785 : NFSubstitution(_pos, _ruleSet, formatter, description, status) |
| 786 { |
| 787 if (description == gEqualsEquals) { |
| 788 // throw new IllegalArgumentException("== is not a legal token"); |
| 789 status = U_PARSE_ERROR; |
| 790 } |
| 791 } |
| 792 |
| 793 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) |
| 794 |
| 795 //=================================================================== |
| 796 // MultiplierSubstitution |
| 797 //=================================================================== |
| 798 |
| 799 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) |
| 800 |
| 801 UBool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const |
| 802 { |
| 803 return NFSubstitution::operator==(rhs) && |
| 804 divisor == ((const MultiplierSubstitution*)&rhs)->divisor; |
| 805 } |
| 806 |
| 807 |
| 808 //=================================================================== |
| 809 // ModulusSubstitution |
| 810 //=================================================================== |
| 811 |
| 812 /** |
| 813 * A substitution that divides the number being formatted by the its rule's |
| 814 * divisor and formats the remainder. Represented by ">>" in a |
| 815 * regular rule. |
| 816 */ |
| 817 ModulusSubstitution::ModulusSubstitution(int32_t _pos, |
| 818 double _divisor, |
| 819 const NFRule* predecessor, |
| 820 const NFRuleSet* _ruleSet, |
| 821 const RuleBasedNumberFormat* formatter, |
| 822 const UnicodeString& description, |
| 823 UErrorCode& status) |
| 824 : NFSubstitution(_pos, _ruleSet, formatter, description, status) |
| 825 , divisor(_divisor) |
| 826 , ruleToUse(NULL) |
| 827 { |
| 828 ldivisor = util64_fromDouble(_divisor); |
| 829 |
| 830 // the owning rule's divisor controls the behavior of this |
| 831 // substitution: rather than keeping a backpointer to the rule, |
| 832 // we keep a copy of the divisor |
| 833 |
| 834 if (ldivisor == 0) { |
| 835 status = U_PARSE_ERROR; |
| 836 } |
| 837 |
| 838 if (description == gGreaterGreaterGreaterThan) { |
| 839 // the >>> token doesn't alter how this substituion calculates the |
| 840 // values it uses for formatting and parsing, but it changes |
| 841 // what's done with that value after it's obtained: >>> short- |
| 842 // circuits the rule-search process and goes straight to the |
| 843 // specified rule to format the substitution value |
| 844 ruleToUse = predecessor; |
| 845 } |
| 846 } |
| 847 |
| 848 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) |
| 849 |
| 850 UBool ModulusSubstitution::operator==(const NFSubstitution& rhs) const |
| 851 { |
| 852 return NFSubstitution::operator==(rhs) && |
| 853 divisor == ((const ModulusSubstitution*)&rhs)->divisor && |
| 854 ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; |
| 855 } |
| 856 |
| 857 //----------------------------------------------------------------------- |
| 858 // formatting |
| 859 //----------------------------------------------------------------------- |
| 860 |
| 861 |
| 862 /** |
| 863 * If this is a >>> substitution, use ruleToUse to fill in |
| 864 * the substitution. Otherwise, just use the superclass function. |
| 865 * @param number The number being formatted |
| 866 * @toInsertInto The string to insert the result of this substitution |
| 867 * into |
| 868 * @param pos The position of the rule text in toInsertInto |
| 869 */ |
| 870 void |
| 871 ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto,
int32_t _pos) const |
| 872 { |
| 873 // if this isn't a >>> substitution, just use the inherited version |
| 874 // of this function (which uses either a rule set or a DecimalFormat |
| 875 // to format its substitution value) |
| 876 if (ruleToUse == NULL) { |
| 877 NFSubstitution::doSubstitution(number, toInsertInto, _pos); |
| 878 |
| 879 // a >>> substitution goes straight to a particular rule to |
| 880 // format the substitution value |
| 881 } else { |
| 882 int64_t numberToFormat = transformNumber(number); |
| 883 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); |
| 884 } |
| 885 } |
| 886 |
| 887 /** |
| 888 * If this is a >>> substitution, use ruleToUse to fill in |
| 889 * the substitution. Otherwise, just use the superclass function. |
| 890 * @param number The number being formatted |
| 891 * @toInsertInto The string to insert the result of this substitution |
| 892 * into |
| 893 * @param pos The position of the rule text in toInsertInto |
| 894 */ |
| 895 void |
| 896 ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto,
int32_t _pos) const |
| 897 { |
| 898 // if this isn't a >>> substitution, just use the inherited version |
| 899 // of this function (which uses either a rule set or a DecimalFormat |
| 900 // to format its substitution value) |
| 901 if (ruleToUse == NULL) { |
| 902 NFSubstitution::doSubstitution(number, toInsertInto, _pos); |
| 903 |
| 904 // a >>> substitution goes straight to a particular rule to |
| 905 // format the substitution value |
| 906 } else { |
| 907 double numberToFormat = transformNumber(number); |
| 908 |
| 909 ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos()); |
| 910 } |
| 911 } |
| 912 |
| 913 //----------------------------------------------------------------------- |
| 914 // parsing |
| 915 //----------------------------------------------------------------------- |
| 916 |
| 917 /** |
| 918 * If this is a >>> substitution, match only against ruleToUse. |
| 919 * Otherwise, use the superclass function. |
| 920 * @param text The string to parse |
| 921 * @param parsePosition Ignored on entry, updated on exit to point to |
| 922 * the first unmatched character. |
| 923 * @param baseValue The partial parse result prior to calling this |
| 924 * routine. |
| 925 */ |
| 926 UBool |
| 927 ModulusSubstitution::doParse(const UnicodeString& text, |
| 928 ParsePosition& parsePosition, |
| 929 double baseValue, |
| 930 double upperBound, |
| 931 UBool lenientParse, |
| 932 Formattable& result) const |
| 933 { |
| 934 // if this isn't a >>> substitution, we can just use the |
| 935 // inherited parse() routine to do the parsing |
| 936 if (ruleToUse == NULL) { |
| 937 return NFSubstitution::doParse(text, parsePosition, baseValue, upperBoun
d, lenientParse, result); |
| 938 |
| 939 // but if it IS a >>> substitution, we have to do it here: we |
| 940 // use the specific rule's doParse() method, and then we have to |
| 941 // do some of the other work of NFRuleSet.parse() |
| 942 } else { |
| 943 ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result); |
| 944 |
| 945 if (parsePosition.getIndex() != 0) { |
| 946 UErrorCode status = U_ZERO_ERROR; |
| 947 double tempResult = result.getDouble(status); |
| 948 tempResult = composeRuleValue(tempResult, baseValue); |
| 949 result.setDouble(tempResult); |
| 950 } |
| 951 |
| 952 return TRUE; |
| 953 } |
| 954 } |
| 955 |
| 956 |
| 957 //=================================================================== |
| 958 // IntegralPartSubstitution |
| 959 //=================================================================== |
| 960 |
| 961 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) |
| 962 |
| 963 |
| 964 //=================================================================== |
| 965 // FractionalPartSubstitution |
| 966 //=================================================================== |
| 967 |
| 968 |
| 969 /** |
| 970 * Constructs a FractionalPartSubstitution. This object keeps a flag |
| 971 * telling whether it should format by digits or not. In addition, |
| 972 * it marks the rule set it calls (if any) as a fraction rule set. |
| 973 */ |
| 974 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, |
| 975 const NFRuleSet* _ruleSet, |
| 976 const RuleBasedNumberFormat* formatter, |
| 977 const UnicodeString& description, |
| 978 UErrorCode& status) |
| 979 : NFSubstitution(_pos, _ruleSet, formatter, description, status) |
| 980 , byDigits(FALSE) |
| 981 , useSpaces(TRUE) |
| 982 |
| 983 { |
| 984 // akk, ruleSet can change in superclass constructor |
| 985 if (description == gGreaterGreaterThan || |
| 986 description == gGreaterGreaterGreaterThan || |
| 987 _ruleSet == getRuleSet()) { |
| 988 byDigits = TRUE; |
| 989 if (description == gGreaterGreaterGreaterThan) { |
| 990 useSpaces = FALSE; |
| 991 } |
| 992 } else { |
| 993 // cast away const |
| 994 ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); |
| 995 } |
| 996 } |
| 997 |
| 998 //----------------------------------------------------------------------- |
| 999 // formatting |
| 1000 //----------------------------------------------------------------------- |
| 1001 |
| 1002 /** |
| 1003 * If in "by digits" mode, fills in the substitution one decimal digit |
| 1004 * at a time using the rule set containing this substitution. |
| 1005 * Otherwise, uses the superclass function. |
| 1006 * @param number The number being formatted |
| 1007 * @param toInsertInto The string to insert the result of formatting |
| 1008 * the substitution into |
| 1009 * @param pos The position of the owning rule's rule text in |
| 1010 * toInsertInto |
| 1011 */ |
| 1012 void |
| 1013 FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInser
tInto, int32_t _pos) const |
| 1014 { |
| 1015 // if we're not in "byDigits" mode, just use the inherited |
| 1016 // doSubstitution() routine |
| 1017 if (!byDigits) { |
| 1018 NFSubstitution::doSubstitution(number, toInsertInto, _pos); |
| 1019 |
| 1020 // if we're in "byDigits" mode, transform the value into an integer |
| 1021 // by moving the decimal point eight places to the right and |
| 1022 // pulling digits off the right one at a time, formatting each digit |
| 1023 // as an integer using this substitution's owning rule set |
| 1024 // (this is slower, but more accurate, than doing it from the |
| 1025 // other end) |
| 1026 } else { |
| 1027 // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(num
ber) * uprv_pow(10, kMaxDecimalDigits)); |
| 1028 // // this flag keeps us from formatting trailing zeros. It starts |
| 1029 // // out false because we're pulling from the right, and switches |
| 1030 // // to true the first time we encounter a non-zero digit |
| 1031 // UBool doZeros = FALSE; |
| 1032 // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { |
| 1033 // int64_t digit = numberToFormat % 10; |
| 1034 // if (digit != 0 || doZeros) { |
| 1035 // if (doZeros && useSpaces) { |
| 1036 // toInsertInto.insert(_pos + getPos(), gSpace); |
| 1037 // } |
| 1038 // doZeros = TRUE; |
| 1039 // getRuleSet()->format(digit, toInsertInto, _pos + getPos(
)); |
| 1040 // } |
| 1041 // numberToFormat /= 10; |
| 1042 // } |
| 1043 |
| 1044 DigitList dl; |
| 1045 dl.set(number); |
| 1046 dl.roundFixedPoint(20); // round to 20 fraction digits. |
| 1047 dl.reduce(); // Removes any trailing zeros. |
| 1048 |
| 1049 UBool pad = FALSE; |
| 1050 for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { |
| 1051 // Loop iterates over fraction digits, starting with the LSD. |
| 1052 // include both real digits from the number, and zeros |
| 1053 // to the left of the MSD but to the right of the decimal point. |
| 1054 if (pad && useSpaces) { |
| 1055 toInsertInto.insert(_pos + getPos(), gSpace); |
| 1056 } else { |
| 1057 pad = TRUE; |
| 1058 } |
| 1059 int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; |
| 1060 getRuleSet()->format(digit, toInsertInto, _pos + getPos()); |
| 1061 } |
| 1062 |
| 1063 if (!pad) { |
| 1064 // hack around lack of precision in digitlist. if we would end up with |
| 1065 // "foo point" make sure we add a " zero" to the end. |
| 1066 getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); |
| 1067 } |
| 1068 } |
| 1069 } |
| 1070 |
| 1071 //----------------------------------------------------------------------- |
| 1072 // parsing |
| 1073 //----------------------------------------------------------------------- |
| 1074 |
| 1075 /** |
| 1076 * If in "by digits" mode, parses the string as if it were a string |
| 1077 * of individual digits; otherwise, uses the superclass function. |
| 1078 * @param text The string to parse |
| 1079 * @param parsePosition Ignored on entry, but updated on exit to point |
| 1080 * to the first unmatched character |
| 1081 * @param baseValue The partial parse result prior to entering this |
| 1082 * function |
| 1083 * @param upperBound Only consider rules with base values lower than |
| 1084 * this when filling in the substitution |
| 1085 * @param lenientParse If true, try matching the text as numerals if |
| 1086 * matching as words doesn't work |
| 1087 * @return If the match was successful, the current partial parse |
| 1088 * result; otherwise new Long(0). The result is either a Long or |
| 1089 * a Double. |
| 1090 */ |
| 1091 |
| 1092 UBool |
| 1093 FractionalPartSubstitution::doParse(const UnicodeString& text, |
| 1094 ParsePosition& parsePosition, |
| 1095 double baseValue, |
| 1096 double /*upperBound*/, |
| 1097 UBool lenientParse, |
| 1098 Formattable& resVal) const |
| 1099 { |
| 1100 // if we're not in byDigits mode, we can just use the inherited |
| 1101 // doParse() |
| 1102 if (!byDigits) { |
| 1103 return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenien
tParse, resVal); |
| 1104 |
| 1105 // if we ARE in byDigits mode, parse the text one digit at a time |
| 1106 // using this substitution's owning rule set (we do this by setting |
| 1107 // upperBound to 10 when calling doParse() ) until we reach |
| 1108 // nonmatching text |
| 1109 } else { |
| 1110 UnicodeString workText(text); |
| 1111 ParsePosition workPos(1); |
| 1112 double result = 0; |
| 1113 int32_t digit; |
| 1114 // double p10 = 0.1; |
| 1115 |
| 1116 DigitList dl; |
| 1117 NumberFormat* fmt = NULL; |
| 1118 while (workText.length() > 0 && workPos.getIndex() != 0) { |
| 1119 workPos.setIndex(0); |
| 1120 Formattable temp; |
| 1121 getRuleSet()->parse(workText, workPos, 10, temp); |
| 1122 UErrorCode status = U_ZERO_ERROR; |
| 1123 digit = temp.getLong(status); |
| 1124 // digit = temp.getType() == Formattable::kLong ? |
| 1125 // temp.getLong() : |
| 1126 // (int32_t)temp.getDouble(); |
| 1127 |
| 1128 if (lenientParse && workPos.getIndex() == 0) { |
| 1129 if (!fmt) { |
| 1130 status = U_ZERO_ERROR; |
| 1131 fmt = NumberFormat::createInstance(status); |
| 1132 if (U_FAILURE(status)) { |
| 1133 delete fmt; |
| 1134 fmt = NULL; |
| 1135 } |
| 1136 } |
| 1137 if (fmt) { |
| 1138 fmt->parse(workText, temp, workPos); |
| 1139 digit = temp.getLong(status); |
| 1140 } |
| 1141 } |
| 1142 |
| 1143 if (workPos.getIndex() != 0) { |
| 1144 dl.append((char)('0' + digit)); |
| 1145 // result += digit * p10; |
| 1146 // p10 /= 10; |
| 1147 parsePosition.setIndex(parsePosition.getIndex() + workPos.getInd
ex()); |
| 1148 workText.removeBetween(0, workPos.getIndex()); |
| 1149 while (workText.length() > 0 && workText.charAt(0) == gSpace) { |
| 1150 workText.removeBetween(0, 1); |
| 1151 parsePosition.setIndex(parsePosition.getIndex() + 1); |
| 1152 } |
| 1153 } |
| 1154 } |
| 1155 delete fmt; |
| 1156 |
| 1157 result = dl.getCount() == 0 ? 0 : dl.getDouble(); |
| 1158 result = composeRuleValue(result, baseValue); |
| 1159 resVal.setDouble(result); |
| 1160 return TRUE; |
| 1161 } |
| 1162 } |
| 1163 |
| 1164 UBool |
| 1165 FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const |
| 1166 { |
| 1167 return NFSubstitution::operator==(rhs) && |
| 1168 ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; |
| 1169 } |
| 1170 |
| 1171 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) |
| 1172 |
| 1173 |
| 1174 //=================================================================== |
| 1175 // AbsoluteValueSubstitution |
| 1176 //=================================================================== |
| 1177 |
| 1178 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) |
| 1179 |
| 1180 //=================================================================== |
| 1181 // NumeratorSubstitution |
| 1182 //=================================================================== |
| 1183 |
| 1184 void |
| 1185 NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto
, int32_t apos) const { |
| 1186 // perform a transformation on the number being formatted that |
| 1187 // is dependent on the type of substitution this is |
| 1188 |
| 1189 double numberToFormat = transformNumber(number); |
| 1190 int64_t longNF = util64_fromDouble(numberToFormat); |
| 1191 |
| 1192 const NFRuleSet* aruleSet = getRuleSet(); |
| 1193 if (withZeros && aruleSet != NULL) { |
| 1194 // if there are leading zeros in the decimal expansion then emit them |
| 1195 int64_t nf =longNF; |
| 1196 int32_t len = toInsertInto.length(); |
| 1197 while ((nf *= 10) < denominator) { |
| 1198 toInsertInto.insert(apos + getPos(), gSpace); |
| 1199 aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); |
| 1200 } |
| 1201 apos += toInsertInto.length() - len; |
| 1202 } |
| 1203 |
| 1204 // if the result is an integer, from here on out we work in integer |
| 1205 // space (saving time and memory and preserving accuracy) |
| 1206 if (numberToFormat == longNF && aruleSet != NULL) { |
| 1207 aruleSet->format(longNF, toInsertInto, apos + getPos()); |
| 1208 |
| 1209 // if the result isn't an integer, then call either our rule set's |
| 1210 // format() method or our DecimalFormat's format() method to |
| 1211 // format the result |
| 1212 } else { |
| 1213 if (aruleSet != NULL) { |
| 1214 aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); |
| 1215 } else { |
| 1216 UErrorCode status = U_ZERO_ERROR; |
| 1217 UnicodeString temp; |
| 1218 getNumberFormat()->format(numberToFormat, temp, status); |
| 1219 toInsertInto.insert(apos + getPos(), temp); |
| 1220 } |
| 1221 } |
| 1222 } |
| 1223 |
| 1224 UBool |
| 1225 NumeratorSubstitution::doParse(const UnicodeString& text, |
| 1226 ParsePosition& parsePosition, |
| 1227 double baseValue, |
| 1228 double upperBound, |
| 1229 UBool /*lenientParse*/, |
| 1230 Formattable& result) const |
| 1231 { |
| 1232 // we don't have to do anything special to do the parsing here, |
| 1233 // but we have to turn lenient parsing off-- if we leave it on, |
| 1234 // it SERIOUSLY messes up the algorithm |
| 1235 |
| 1236 // if withZeros is true, we need to count the zeros |
| 1237 // and use that to adjust the parse result |
| 1238 UErrorCode status = U_ZERO_ERROR; |
| 1239 int32_t zeroCount = 0; |
| 1240 UnicodeString workText(text); |
| 1241 |
| 1242 if (withZeros) { |
| 1243 ParsePosition workPos(1); |
| 1244 Formattable temp; |
| 1245 |
| 1246 while (workText.length() > 0 && workPos.getIndex() != 0) { |
| 1247 workPos.setIndex(0); |
| 1248 getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or no
thing at all |
| 1249 if (workPos.getIndex() == 0) { |
| 1250 // we failed, either there were no more zeros, or the number was
formatted with digits |
| 1251 // either way, we're done |
| 1252 break; |
| 1253 } |
| 1254 |
| 1255 ++zeroCount; |
| 1256 parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()
); |
| 1257 workText.remove(0, workPos.getIndex()); |
| 1258 while (workText.length() > 0 && workText.charAt(0) == gSpace) { |
| 1259 workText.remove(0, 1); |
| 1260 parsePosition.setIndex(parsePosition.getIndex() + 1); |
| 1261 } |
| 1262 } |
| 1263 |
| 1264 workText = text; |
| 1265 workText.remove(0, (int32_t)parsePosition.getIndex()); |
| 1266 parsePosition.setIndex(0); |
| 1267 } |
| 1268 |
| 1269 // we've parsed off the zeros, now let's parse the rest from our current pos
ition |
| 1270 NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue,
upperBound, FALSE, result); |
| 1271 |
| 1272 if (withZeros) { |
| 1273 // any base value will do in this case. is there a way to |
| 1274 // force this to not bother trying all the base values? |
| 1275 |
| 1276 // compute the 'effective' base and prescale the value down |
| 1277 int64_t n = result.getLong(status); // force conversion! |
| 1278 int64_t d = 1; |
| 1279 int32_t pow = 0; |
| 1280 while (d <= n) { |
| 1281 d *= 10; |
| 1282 ++pow; |
| 1283 } |
| 1284 // now add the zeros |
| 1285 while (zeroCount > 0) { |
| 1286 d *= 10; |
| 1287 --zeroCount; |
| 1288 } |
| 1289 // d is now our true denominator |
| 1290 result.setDouble((double)n/(double)d); |
| 1291 } |
| 1292 |
| 1293 return TRUE; |
| 1294 } |
| 1295 |
| 1296 UBool |
| 1297 NumeratorSubstitution::operator==(const NFSubstitution& rhs) const |
| 1298 { |
| 1299 return NFSubstitution::operator==(rhs) && |
| 1300 denominator == ((const NumeratorSubstitution*)&rhs)->denominator; |
| 1301 } |
| 1302 |
| 1303 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) |
| 1304 |
| 1305 const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; |
| 1306 |
| 1307 //=================================================================== |
| 1308 // NullSubstitution |
| 1309 //=================================================================== |
| 1310 |
| 1311 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution) |
| 1312 |
| 1313 U_NAMESPACE_END |
| 1314 |
| 1315 /* U_HAVE_RBNF */ |
| 1316 #endif |
| 1317 |
OLD | NEW |