intl/icu/source/i18n/nfsubs.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial