1 /* 2 ****************************************************************************** 3 * Copyright (C) 1997-2014, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ****************************************************************************** 6 * file name: nfrule.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 "nfrule.h" 17 18 #if U_HAVE_RBNF 19 20 #include "unicode/localpointer.h" 21 #include "unicode/rbnf.h" 22 #include "unicode/tblcoll.h" 23 #include "unicode/coleitr.h" 24 #include "unicode/uchar.h" 25 #include "nfrs.h" 26 #include "nfrlist.h" 27 #include "nfsubs.h" 28 #include "patternprops.h" 29 30 U_NAMESPACE_BEGIN 31 32 NFRule::NFRule(const RuleBasedNumberFormat* _rbnf) 33 : baseValue((int32_t)0) 34 , radix(0) 35 , exponent(0) 36 , ruleText() 37 , sub1(NULL) 38 , sub2(NULL) 39 , formatter(_rbnf) 40 { 41 } 42 43 NFRule::~NFRule() 44 { 45 delete sub1; 46 delete sub2; 47 } 48 49 static const UChar gLeftBracket = 0x005b; 50 static const UChar gRightBracket = 0x005d; 51 static const UChar gColon = 0x003a; 52 static const UChar gZero = 0x0030; 53 static const UChar gNine = 0x0039; 54 static const UChar gSpace = 0x0020; 55 static const UChar gSlash = 0x002f; 56 static const UChar gGreaterThan = 0x003e; 57 static const UChar gLessThan = 0x003c; 58 static const UChar gComma = 0x002c; 59 static const UChar gDot = 0x002e; 60 static const UChar gTick = 0x0027; 61 //static const UChar gMinus = 0x002d; 62 static const UChar gSemicolon = 0x003b; 63 64 static const UChar gMinusX[] = {0x2D, 0x78, 0}; /* "-x" */ 65 static const UChar gXDotX[] = {0x78, 0x2E, 0x78, 0}; /* "x.x" */ 66 static const UChar gXDotZero[] = {0x78, 0x2E, 0x30, 0}; /* "x.0" */ 67 static const UChar gZeroDotX[] = {0x30, 0x2E, 0x78, 0}; /* "0.x" */ 68 69 static const UChar gLessLess[] = {0x3C, 0x3C, 0}; /* "<<" */ 70 static const UChar gLessPercent[] = {0x3C, 0x25, 0}; /* "<%" */ 71 static const UChar gLessHash[] = {0x3C, 0x23, 0}; /* "<#" */ 72 static const UChar gLessZero[] = {0x3C, 0x30, 0}; /* "<0" */ 73 static const UChar gGreaterGreater[] = {0x3E, 0x3E, 0}; /* ">>" */ 74 static const UChar gGreaterPercent[] = {0x3E, 0x25, 0}; /* ">%" */ 75 static const UChar gGreaterHash[] = {0x3E, 0x23, 0}; /* ">#" */ 76 static const UChar gGreaterZero[] = {0x3E, 0x30, 0}; /* ">0" */ 77 static const UChar gEqualPercent[] = {0x3D, 0x25, 0}; /* "=%" */ 78 static const UChar gEqualHash[] = {0x3D, 0x23, 0}; /* "=#" */ 79 static const UChar gEqualZero[] = {0x3D, 0x30, 0}; /* "=0" */ 80 static const UChar gGreaterGreaterGreater[] = {0x3E, 0x3E, 0x3E, 0}; /* ">>>" */ 81 82 static const UChar * const tokenStrings[] = { 83 gLessLess, gLessPercent, gLessHash, gLessZero, 84 gGreaterGreater, gGreaterPercent,gGreaterHash, gGreaterZero, 85 gEqualPercent, gEqualHash, gEqualZero, NULL 86 }; 87 88 void 89 NFRule::makeRules(UnicodeString& description, 90 const NFRuleSet *ruleSet, 91 const NFRule *predecessor, 92 const RuleBasedNumberFormat *rbnf, 93 NFRuleList& rules, 94 UErrorCode& status) 95 { 96 // we know we're making at least one rule, so go ahead and 97 // new it up and initialize its basevalue and divisor 98 // (this also strips the rule descriptor, if any, off the 99 // descripton string) 100 NFRule* rule1 = new NFRule(rbnf); 101 /* test for NULL */ 102 if (rule1 == 0) { 103 status = U_MEMORY_ALLOCATION_ERROR; 104 return; 105 } 106 rule1->parseRuleDescriptor(description, status); 107 108 // check the description to see whether there's text enclosed 109 // in brackets 110 int32_t brack1 = description.indexOf(gLeftBracket); 111 int32_t brack2 = description.indexOf(gRightBracket); 112 113 // if the description doesn't contain a matched pair of brackets, 114 // or if it's of a type that doesn't recognize bracketed text, 115 // then leave the description alone, initialize the rule's 116 // rule text and substitutions, and return that rule 117 if (brack1 == -1 || brack2 == -1 || brack1 > brack2 118 || rule1->getType() == kProperFractionRule 119 || rule1->getType() == kNegativeNumberRule) { 120 rule1->ruleText = description; 121 rule1->extractSubstitutions(ruleSet, predecessor, rbnf, status); 122 rules.add(rule1); 123 } else { 124 // if the description does contain a matched pair of brackets, 125 // then it's really shorthand for two rules (with one exception) 126 NFRule* rule2 = NULL; 127 UnicodeString sbuf; 128 129 // we'll actually only split the rule into two rules if its 130 // base value is an even multiple of its divisor (or it's one 131 // of the special rules) 132 if ((rule1->baseValue > 0 133 && (rule1->baseValue % util64_pow(rule1->radix, rule1->exponent)) == 0) 134 || rule1->getType() == kImproperFractionRule 135 || rule1->getType() == kMasterRule) { 136 137 // if it passes that test, new up the second rule. If the 138 // rule set both rules will belong to is a fraction rule 139 // set, they both have the same base value; otherwise, 140 // increment the original rule's base value ("rule1" actually 141 // goes SECOND in the rule set's rule list) 142 rule2 = new NFRule(rbnf); 143 /* test for NULL */ 144 if (rule2 == 0) { 145 status = U_MEMORY_ALLOCATION_ERROR; 146 return; 147 } 148 if (rule1->baseValue >= 0) { 149 rule2->baseValue = rule1->baseValue; 150 if (!ruleSet->isFractionRuleSet()) { 151 ++rule1->baseValue; 152 } 153 } 154 155 // if the description began with "x.x" and contains bracketed 156 // text, it describes both the improper fraction rule and 157 // the proper fraction rule 158 else if (rule1->getType() == kImproperFractionRule) { 159 rule2->setType(kProperFractionRule); 160 } 161 162 // if the description began with "x.0" and contains bracketed 163 // text, it describes both the master rule and the 164 // improper fraction rule 165 else if (rule1->getType() == kMasterRule) { 166 rule2->baseValue = rule1->baseValue; 167 rule1->setType(kImproperFractionRule); 168 } 169 170 // both rules have the same radix and exponent (i.e., the 171 // same divisor) 172 rule2->radix = rule1->radix; 173 rule2->exponent = rule1->exponent; 174 175 // rule2's rule text omits the stuff in brackets: initalize 176 // its rule text and substitutions accordingly 177 sbuf.append(description, 0, brack1); 178 if (brack2 + 1 < description.length()) { 179 sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); 180 } 181 rule2->ruleText.setTo(sbuf); 182 rule2->extractSubstitutions(ruleSet, predecessor, rbnf, status); 183 } 184 185 // rule1's text includes the text in the brackets but omits 186 // the brackets themselves: initialize _its_ rule text and 187 // substitutions accordingly 188 sbuf.setTo(description, 0, brack1); 189 sbuf.append(description, brack1 + 1, brack2 - brack1 - 1); 190 if (brack2 + 1 < description.length()) { 191 sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); 192 } 193 rule1->ruleText.setTo(sbuf); 194 rule1->extractSubstitutions(ruleSet, predecessor, rbnf, status); 195 196 // if we only have one rule, return it; if we have two, return 197 // a two-element array containing them (notice that rule2 goes 198 // BEFORE rule1 in the list: in all cases, rule2 OMITS the 199 // material in the brackets and rule1 INCLUDES the material 200 // in the brackets) 201 if (rule2 != NULL) { 202 rules.add(rule2); 203 } 204 rules.add(rule1); 205 } 206 } 207 208 /** 209 * This function parses the rule's rule descriptor (i.e., the base 210 * value and/or other tokens that precede the rule's rule text 211 * in the description) and sets the rule's base value, radix, and 212 * exponent according to the descriptor. (If the description doesn't 213 * include a rule descriptor, then this function sets everything to 214 * default values and the rule set sets the rule's real base value). 215 * @param description The rule's description 216 * @return If "description" included a rule descriptor, this is 217 * "description" with the descriptor and any trailing whitespace 218 * stripped off. Otherwise; it's "descriptor" unchangd. 219 */ 220 void 221 NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status) 222 { 223 // the description consists of a rule descriptor and a rule body, 224 // separated by a colon. The rule descriptor is optional. If 225 // it's omitted, just set the base value to 0. 226 int32_t p = description.indexOf(gColon); 227 if (p == -1) { 228 setBaseValue((int32_t)0, status); 229 } else { 230 // copy the descriptor out into its own string and strip it, 231 // along with any trailing whitespace, out of the original 232 // description 233 UnicodeString descriptor; 234 descriptor.setTo(description, 0, p); 235 236 ++p; 237 while (p < description.length() && PatternProps::isWhiteSpace(description.charAt(p))) { 238 ++p; 239 } 240 description.removeBetween(0, p); 241 242 // check first to see if the rule descriptor matches the token 243 // for one of the special rules. If it does, set the base 244 // value to the correct identfier value 245 if (0 == descriptor.compare(gMinusX, 2)) { 246 setType(kNegativeNumberRule); 247 } 248 else if (0 == descriptor.compare(gXDotX, 3)) { 249 setType(kImproperFractionRule); 250 } 251 else if (0 == descriptor.compare(gZeroDotX, 3)) { 252 setType(kProperFractionRule); 253 } 254 else if (0 == descriptor.compare(gXDotZero, 3)) { 255 setType(kMasterRule); 256 } 257 258 // if the rule descriptor begins with a digit, it's a descriptor 259 // for a normal rule 260 // since we don't have Long.parseLong, and this isn't much work anyway, 261 // just build up the value as we encounter the digits. 262 else if (descriptor.charAt(0) >= gZero && descriptor.charAt(0) <= gNine) { 263 int64_t val = 0; 264 p = 0; 265 UChar c = gSpace; 266 267 // begin parsing the descriptor: copy digits 268 // into "tempValue", skip periods, commas, and spaces, 269 // stop on a slash or > sign (or at the end of the string), 270 // and throw an exception on any other character 271 int64_t ll_10 = 10; 272 while (p < descriptor.length()) { 273 c = descriptor.charAt(p); 274 if (c >= gZero && c <= gNine) { 275 val = val * ll_10 + (int32_t)(c - gZero); 276 } 277 else if (c == gSlash || c == gGreaterThan) { 278 break; 279 } 280 else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { 281 } 282 else { 283 // throw new IllegalArgumentException("Illegal character in rule descriptor"); 284 status = U_PARSE_ERROR; 285 return; 286 } 287 ++p; 288 } 289 290 // we have the base value, so set it 291 setBaseValue(val, status); 292 293 // if we stopped the previous loop on a slash, we're 294 // now parsing the rule's radix. Again, accumulate digits 295 // in tempValue, skip punctuation, stop on a > mark, and 296 // throw an exception on anything else 297 if (c == gSlash) { 298 val = 0; 299 ++p; 300 int64_t ll_10 = 10; 301 while (p < descriptor.length()) { 302 c = descriptor.charAt(p); 303 if (c >= gZero && c <= gNine) { 304 val = val * ll_10 + (int32_t)(c - gZero); 305 } 306 else if (c == gGreaterThan) { 307 break; 308 } 309 else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { 310 } 311 else { 312 // throw new IllegalArgumentException("Illegal character is rule descriptor"); 313 status = U_PARSE_ERROR; 314 return; 315 } 316 ++p; 317 } 318 319 // tempValue now contain's the rule's radix. Set it 320 // accordingly, and recalculate the rule's exponent 321 radix = (int32_t)val; 322 if (radix == 0) { 323 // throw new IllegalArgumentException("Rule can't have radix of 0"); 324 status = U_PARSE_ERROR; 325 } 326 327 exponent = expectedExponent(); 328 } 329 330 // if we stopped the previous loop on a > sign, then continue 331 // for as long as we still see > signs. For each one, 332 // decrement the exponent (unless the exponent is already 0). 333 // If we see another character before reaching the end of 334 // the descriptor, that's also a syntax error. 335 if (c == gGreaterThan) { 336 while (p < descriptor.length()) { 337 c = descriptor.charAt(p); 338 if (c == gGreaterThan && exponent > 0) { 339 --exponent; 340 } else { 341 // throw new IllegalArgumentException("Illegal character in rule descriptor"); 342 status = U_PARSE_ERROR; 343 return; 344 } 345 ++p; 346 } 347 } 348 } 349 } 350 351 // finally, if the rule body begins with an apostrophe, strip it off 352 // (this is generally used to put whitespace at the beginning of 353 // a rule's rule text) 354 if (description.length() > 0 && description.charAt(0) == gTick) { 355 description.removeBetween(0, 1); 356 } 357 358 // return the description with all the stuff we've just waded through 359 // stripped off the front. It now contains just the rule body. 360 // return description; 361 } 362 363 /** 364 * Searches the rule's rule text for the substitution tokens, 365 * creates the substitutions, and removes the substitution tokens 366 * from the rule's rule text. 367 * @param owner The rule set containing this rule 368 * @param predecessor The rule preseding this one in "owners" rule list 369 * @param ownersOwner The RuleBasedFormat that owns this rule 370 */ 371 void 372 NFRule::extractSubstitutions(const NFRuleSet* ruleSet, 373 const NFRule* predecessor, 374 const RuleBasedNumberFormat* rbnf, 375 UErrorCode& status) 376 { 377 if (U_SUCCESS(status)) { 378 sub1 = extractSubstitution(ruleSet, predecessor, rbnf, status); 379 sub2 = extractSubstitution(ruleSet, predecessor, rbnf, status); 380 } 381 } 382 383 /** 384 * Searches the rule's rule text for the first substitution token, 385 * creates a substitution based on it, and removes the token from 386 * the rule's rule text. 387 * @param owner The rule set containing this rule 388 * @param predecessor The rule preceding this one in the rule set's 389 * rule list 390 * @param ownersOwner The RuleBasedNumberFormat that owns this rule 391 * @return The newly-created substitution. This is never null; if 392 * the rule text doesn't contain any substitution tokens, this will 393 * be a NullSubstitution. 394 */ 395 NFSubstitution * 396 NFRule::extractSubstitution(const NFRuleSet* ruleSet, 397 const NFRule* predecessor, 398 const RuleBasedNumberFormat* rbnf, 399 UErrorCode& status) 400 { 401 NFSubstitution* result = NULL; 402 403 // search the rule's rule text for the first two characters of 404 // a substitution token 405 int32_t subStart = indexOfAny(tokenStrings); 406 int32_t subEnd = subStart; 407 408 // if we didn't find one, create a null substitution positioned 409 // at the end of the rule text 410 if (subStart == -1) { 411 return NFSubstitution::makeSubstitution(ruleText.length(), this, predecessor, 412 ruleSet, rbnf, UnicodeString(), status); 413 } 414 415 // special-case the ">>>" token, since searching for the > at the 416 // end will actually find the > in the middle 417 if (ruleText.indexOf(gGreaterGreaterGreater, 3, 0) == subStart) { 418 subEnd = subStart + 2; 419 420 // otherwise the substitution token ends with the same character 421 // it began with 422 } else { 423 UChar c = ruleText.charAt(subStart); 424 subEnd = ruleText.indexOf(c, subStart + 1); 425 // special case for '<%foo<<' 426 if (c == gLessThan && subEnd != -1 && subEnd < ruleText.length() - 1 && ruleText.charAt(subEnd+1) == c) { 427 // ordinals use "=#,##0==%abbrev=" as their rule. Notice that the '==' in the middle 428 // occurs because of the juxtaposition of two different rules. The check for '<' is a hack 429 // to get around this. Having the duplicate at the front would cause problems with 430 // rules like "<<%" to format, say, percents... 431 ++subEnd; 432 } 433 } 434 435 // if we don't find the end of the token (i.e., if we're on a single, 436 // unmatched token character), create a null substitution positioned 437 // at the end of the rule 438 if (subEnd == -1) { 439 return NFSubstitution::makeSubstitution(ruleText.length(), this, predecessor, 440 ruleSet, rbnf, UnicodeString(), status); 441 } 442 443 // if we get here, we have a real substitution token (or at least 444 // some text bounded by substitution token characters). Use 445 // makeSubstitution() to create the right kind of substitution 446 UnicodeString subToken; 447 subToken.setTo(ruleText, subStart, subEnd + 1 - subStart); 448 result = NFSubstitution::makeSubstitution(subStart, this, predecessor, ruleSet, 449 rbnf, subToken, status); 450 451 // remove the substitution from the rule text 452 ruleText.removeBetween(subStart, subEnd+1); 453 454 return result; 455 } 456 457 /** 458 * Sets the rule's base value, and causes the radix and exponent 459 * to be recalculated. This is used during construction when we 460 * don't know the rule's base value until after it's been 461 * constructed. It should be used at any other time. 462 * @param The new base value for the rule. 463 */ 464 void 465 NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status) 466 { 467 // set the base value 468 baseValue = newBaseValue; 469 470 // if this isn't a special rule, recalculate the radix and exponent 471 // (the radix always defaults to 10; if it's supposed to be something 472 // else, it's cleaned up by the caller and the exponent is 473 // recalculated again-- the only function that does this is 474 // NFRule.parseRuleDescriptor() ) 475 if (baseValue >= 1) { 476 radix = 10; 477 exponent = expectedExponent(); 478 479 // this function gets called on a fully-constructed rule whose 480 // description didn't specify a base value. This means it 481 // has substitutions, and some substitutions hold on to copies 482 // of the rule's divisor. Fix their copies of the divisor. 483 if (sub1 != NULL) { 484 sub1->setDivisor(radix, exponent, status); 485 } 486 if (sub2 != NULL) { 487 sub2->setDivisor(radix, exponent, status); 488 } 489 490 // if this is a special rule, its radix and exponent are basically 491 // ignored. Set them to "safe" default values 492 } else { 493 radix = 10; 494 exponent = 0; 495 } 496 } 497 498 /** 499 * This calculates the rule's exponent based on its radix and base 500 * value. This will be the highest power the radix can be raised to 501 * and still produce a result less than or equal to the base value. 502 */ 503 int16_t 504 NFRule::expectedExponent() const 505 { 506 // since the log of 0, or the log base 0 of something, causes an 507 // error, declare the exponent in these cases to be 0 (we also 508 // deal with the special-rule identifiers here) 509 if (radix == 0 || baseValue < 1) { 510 return 0; 511 } 512 513 // we get rounding error in some cases-- for example, log 1000 / log 10 514 // gives us 1.9999999996 instead of 2. The extra logic here is to take 515 // that into account 516 int16_t tempResult = (int16_t)(uprv_log((double)baseValue) / uprv_log((double)radix)); 517 int64_t temp = util64_pow(radix, tempResult + 1); 518 if (temp <= baseValue) { 519 tempResult += 1; 520 } 521 return tempResult; 522 } 523 524 /** 525 * Searches the rule's rule text for any of the specified strings. 526 * @param strings An array of strings to search the rule's rule 527 * text for 528 * @return The index of the first match in the rule's rule text 529 * (i.e., the first substring in the rule's rule text that matches 530 * _any_ of the strings in "strings"). If none of the strings in 531 * "strings" is found in the rule's rule text, returns -1. 532 */ 533 int32_t 534 NFRule::indexOfAny(const UChar* const strings[]) const 535 { 536 int result = -1; 537 for (int i = 0; strings[i]; i++) { 538 int32_t pos = ruleText.indexOf(*strings[i]); 539 if (pos != -1 && (result == -1 || pos < result)) { 540 result = pos; 541 } 542 } 543 return result; 544 } 545 546 //----------------------------------------------------------------------- 547 // boilerplate 548 //----------------------------------------------------------------------- 549 550 /** 551 * Tests two rules for equality. 552 * @param that The rule to compare this one against 553 * @return True is the two rules are functionally equivalent 554 */ 555 UBool 556 NFRule::operator==(const NFRule& rhs) const 557 { 558 return baseValue == rhs.baseValue 559 && radix == rhs.radix 560 && exponent == rhs.exponent 561 && ruleText == rhs.ruleText 562 && *sub1 == *rhs.sub1 563 && *sub2 == *rhs.sub2; 564 } 565 566 /** 567 * Returns a textual representation of the rule. This won't 568 * necessarily be the same as the description that this rule 569 * was created with, but it will produce the same result. 570 * @return A textual description of the rule 571 */ 572 static void util_append64(UnicodeString& result, int64_t n) 573 { 574 UChar buffer[256]; 575 int32_t len = util64_tou(n, buffer, sizeof(buffer)); 576 UnicodeString temp(buffer, len); 577 result.append(temp); 578 } 579 580 void 581 NFRule::_appendRuleText(UnicodeString& result) const 582 { 583 switch (getType()) { 584 case kNegativeNumberRule: result.append(gMinusX, 2); break; 585 case kImproperFractionRule: result.append(gXDotX, 3); break; 586 case kProperFractionRule: result.append(gZeroDotX, 3); break; 587 case kMasterRule: result.append(gXDotZero, 3); break; 588 default: 589 // for a normal rule, write out its base value, and if the radix is 590 // something other than 10, write out the radix (with the preceding 591 // slash, of course). Then calculate the expected exponent and if 592 // if isn't the same as the actual exponent, write an appropriate 593 // number of > signs. Finally, terminate the whole thing with 594 // a colon. 595 util_append64(result, baseValue); 596 if (radix != 10) { 597 result.append(gSlash); 598 util_append64(result, radix); 599 } 600 int numCarets = expectedExponent() - exponent; 601 for (int i = 0; i < numCarets; i++) { 602 result.append(gGreaterThan); 603 } 604 break; 605 } 606 result.append(gColon); 607 result.append(gSpace); 608 609 // if the rule text begins with a space, write an apostrophe 610 // (whitespace after the rule descriptor is ignored; the 611 // apostrophe is used to make the whitespace significant) 612 if (ruleText.charAt(0) == gSpace && sub1->getPos() != 0) { 613 result.append(gTick); 614 } 615 616 // now, write the rule's rule text, inserting appropriate 617 // substitution tokens in the appropriate places 618 UnicodeString ruleTextCopy; 619 ruleTextCopy.setTo(ruleText); 620 621 UnicodeString temp; 622 sub2->toString(temp); 623 ruleTextCopy.insert(sub2->getPos(), temp); 624 sub1->toString(temp); 625 ruleTextCopy.insert(sub1->getPos(), temp); 626 627 result.append(ruleTextCopy); 628 629 // and finally, top the whole thing off with a semicolon and 630 // return the result 631 result.append(gSemicolon); 632 } 633 634 //----------------------------------------------------------------------- 635 // formatting 636 //----------------------------------------------------------------------- 637 638 /** 639 * Formats the number, and inserts the resulting text into 640 * toInsertInto. 641 * @param number The number being formatted 642 * @param toInsertInto The string where the resultant text should 643 * be inserted 644 * @param pos The position in toInsertInto where the resultant text 645 * should be inserted 646 */ 647 void 648 NFRule::doFormat(int64_t number, UnicodeString& toInsertInto, int32_t pos) const 649 { 650 // first, insert the rule's rule text into toInsertInto at the 651 // specified position, then insert the results of the substitutions 652 // into the right places in toInsertInto (notice we do the 653 // substitutions in reverse order so that the offsets don't get 654 // messed up) 655 toInsertInto.insert(pos, ruleText); 656 sub2->doSubstitution(number, toInsertInto, pos); 657 sub1->doSubstitution(number, toInsertInto, pos); 658 } 659 660 /** 661 * Formats the number, and inserts the resulting text into 662 * toInsertInto. 663 * @param number The number being formatted 664 * @param toInsertInto The string where the resultant text should 665 * be inserted 666 * @param pos The position in toInsertInto where the resultant text 667 * should be inserted 668 */ 669 void 670 NFRule::doFormat(double number, UnicodeString& toInsertInto, int32_t pos) const 671 { 672 // first, insert the rule's rule text into toInsertInto at the 673 // specified position, then insert the results of the substitutions 674 // into the right places in toInsertInto 675 // [again, we have two copies of this routine that do the same thing 676 // so that we don't sacrifice precision in a long by casting it 677 // to a double] 678 toInsertInto.insert(pos, ruleText); 679 sub2->doSubstitution(number, toInsertInto, pos); 680 sub1->doSubstitution(number, toInsertInto, pos); 681 } 682 683 /** 684 * Used by the owning rule set to determine whether to invoke the 685 * rollback rule (i.e., whether this rule or the one that precedes 686 * it in the rule set's list should be used to format the number) 687 * @param The number being formatted 688 * @return True if the rule set should use the rule that precedes 689 * this one in its list; false if it should use this rule 690 */ 691 UBool 692 NFRule::shouldRollBack(double number) const 693 { 694 // we roll back if the rule contains a modulus substitution, 695 // the number being formatted is an even multiple of the rule's 696 // divisor, and the rule's base value is NOT an even multiple 697 // of its divisor 698 // In other words, if the original description had 699 // 100: << hundred[ >>]; 700 // that expands into 701 // 100: << hundred; 702 // 101: << hundred >>; 703 // internally. But when we're formatting 200, if we use the rule 704 // at 101, which would normally apply, we get "two hundred zero". 705 // To prevent this, we roll back and use the rule at 100 instead. 706 // This is the logic that makes this happen: the rule at 101 has 707 // a modulus substitution, its base value isn't an even multiple 708 // of 100, and the value we're trying to format _is_ an even 709 // multiple of 100. This is called the "rollback rule." 710 if ((sub1->isModulusSubstitution()) || (sub2->isModulusSubstitution())) { 711 int64_t re = util64_pow(radix, exponent); 712 return uprv_fmod(number, (double)re) == 0 && (baseValue % re) != 0; 713 } 714 return FALSE; 715 } 716 717 //----------------------------------------------------------------------- 718 // parsing 719 //----------------------------------------------------------------------- 720 721 /** 722 * Attempts to parse the string with this rule. 723 * @param text The string being parsed 724 * @param parsePosition On entry, the value is ignored and assumed to 725 * be 0. On exit, this has been updated with the position of the first 726 * character not consumed by matching the text against this rule 727 * (if this rule doesn't match the text at all, the parse position 728 * if left unchanged (presumably at 0) and the function returns 729 * new Long(0)). 730 * @param isFractionRule True if this rule is contained within a 731 * fraction rule set. This is only used if the rule has no 732 * substitutions. 733 * @return If this rule matched the text, this is the rule's base value 734 * combined appropriately with the results of parsing the substitutions. 735 * If nothing matched, this is new Long(0) and the parse position is 736 * left unchanged. The result will be an instance of Long if the 737 * result is an integer and Double otherwise. The result is never null. 738 */ 739 #ifdef RBNF_DEBUG 740 #include <stdio.h> 741 742 static void dumpUS(FILE* f, const UnicodeString& us) { 743 int len = us.length(); 744 char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1]; 745 if (buf != NULL) { 746 us.extract(0, len, buf); 747 buf[len] = 0; 748 fprintf(f, "%s", buf); 749 uprv_free(buf); //delete[] buf; 750 } 751 } 752 #endif 753 754 UBool 755 NFRule::doParse(const UnicodeString& text, 756 ParsePosition& parsePosition, 757 UBool isFractionRule, 758 double upperBound, 759 Formattable& resVal) const 760 { 761 // internally we operate on a copy of the string being parsed 762 // (because we're going to change it) and use our own ParsePosition 763 ParsePosition pp; 764 UnicodeString workText(text); 765 766 // check to see whether the text before the first substitution 767 // matches the text at the beginning of the string being 768 // parsed. If it does, strip that off the front of workText; 769 // otherwise, dump out with a mismatch 770 UnicodeString prefix; 771 prefix.setTo(ruleText, 0, sub1->getPos()); 772 773 #ifdef RBNF_DEBUG 774 fprintf(stderr, "doParse %x ", this); 775 { 776 UnicodeString rt; 777 _appendRuleText(rt); 778 dumpUS(stderr, rt); 779 } 780 781 fprintf(stderr, " text: '", this); 782 dumpUS(stderr, text); 783 fprintf(stderr, "' prefix: '"); 784 dumpUS(stderr, prefix); 785 #endif 786 stripPrefix(workText, prefix, pp); 787 int32_t prefixLength = text.length() - workText.length(); 788 789 #ifdef RBNF_DEBUG 790 fprintf(stderr, "' pl: %d ppi: %d s1p: %d\n", prefixLength, pp.getIndex(), sub1->getPos()); 791 #endif 792 793 if (pp.getIndex() == 0 && sub1->getPos() != 0) { 794 // commented out because ParsePosition doesn't have error index in 1.1.x 795 // restored for ICU4C port 796 parsePosition.setErrorIndex(pp.getErrorIndex()); 797 resVal.setLong(0); 798 return TRUE; 799 } 800 801 // this is the fun part. The basic guts of the rule-matching 802 // logic is matchToDelimiter(), which is called twice. The first 803 // time it searches the input string for the rule text BETWEEN 804 // the substitutions and tries to match the intervening text 805 // in the input string with the first substitution. If that 806 // succeeds, it then calls it again, this time to look for the 807 // rule text after the second substitution and to match the 808 // intervening input text against the second substitution. 809 // 810 // For example, say we have a rule that looks like this: 811 // first << middle >> last; 812 // and input text that looks like this: 813 // first one middle two last 814 // First we use stripPrefix() to match "first " in both places and 815 // strip it off the front, leaving 816 // one middle two last 817 // Then we use matchToDelimiter() to match " middle " and try to 818 // match "one" against a substitution. If it's successful, we now 819 // have 820 // two last 821 // We use matchToDelimiter() a second time to match " last" and 822 // try to match "two" against a substitution. If "two" matches 823 // the substitution, we have a successful parse. 824 // 825 // Since it's possible in many cases to find multiple instances 826 // of each of these pieces of rule text in the input string, 827 // we need to try all the possible combinations of these 828 // locations. This prevents us from prematurely declaring a mismatch, 829 // and makes sure we match as much input text as we can. 830 int highWaterMark = 0; 831 double result = 0; 832 int start = 0; 833 double tempBaseValue = (double)(baseValue <= 0 ? 0 : baseValue); 834 835 UnicodeString temp; 836 do { 837 // our partial parse result starts out as this rule's base 838 // value. If it finds a successful match, matchToDelimiter() 839 // will compose this in some way with what it gets back from 840 // the substitution, giving us a new partial parse result 841 pp.setIndex(0); 842 843 temp.setTo(ruleText, sub1->getPos(), sub2->getPos() - sub1->getPos()); 844 double partialResult = matchToDelimiter(workText, start, tempBaseValue, 845 temp, pp, sub1, 846 upperBound); 847 848 // if we got a successful match (or were trying to match a 849 // null substitution), pp is now pointing at the first unmatched 850 // character. Take note of that, and try matchToDelimiter() 851 // on the input text again 852 if (pp.getIndex() != 0 || sub1->isNullSubstitution()) { 853 start = pp.getIndex(); 854 855 UnicodeString workText2; 856 workText2.setTo(workText, pp.getIndex(), workText.length() - pp.getIndex()); 857 ParsePosition pp2; 858 859 // the second matchToDelimiter() will compose our previous 860 // partial result with whatever it gets back from its 861 // substitution if there's a successful match, giving us 862 // a real result 863 temp.setTo(ruleText, sub2->getPos(), ruleText.length() - sub2->getPos()); 864 partialResult = matchToDelimiter(workText2, 0, partialResult, 865 temp, pp2, sub2, 866 upperBound); 867 868 // if we got a successful match on this second 869 // matchToDelimiter() call, update the high-water mark 870 // and result (if necessary) 871 if (pp2.getIndex() != 0 || sub2->isNullSubstitution()) { 872 if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) { 873 highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex(); 874 result = partialResult; 875 } 876 } 877 // commented out because ParsePosition doesn't have error index in 1.1.x 878 // restored for ICU4C port 879 else { 880 int32_t temp = pp2.getErrorIndex() + sub1->getPos() + pp.getIndex(); 881 if (temp> parsePosition.getErrorIndex()) { 882 parsePosition.setErrorIndex(temp); 883 } 884 } 885 } 886 // commented out because ParsePosition doesn't have error index in 1.1.x 887 // restored for ICU4C port 888 else { 889 int32_t temp = sub1->getPos() + pp.getErrorIndex(); 890 if (temp > parsePosition.getErrorIndex()) { 891 parsePosition.setErrorIndex(temp); 892 } 893 } 894 // keep trying to match things until the outer matchToDelimiter() 895 // call fails to make a match (each time, it picks up where it 896 // left off the previous time) 897 } while (sub1->getPos() != sub2->getPos() 898 && pp.getIndex() > 0 899 && pp.getIndex() < workText.length() 900 && pp.getIndex() != start); 901 902 // update the caller's ParsePosition with our high-water mark 903 // (i.e., it now points at the first character this function 904 // didn't match-- the ParsePosition is therefore unchanged if 905 // we didn't match anything) 906 parsePosition.setIndex(highWaterMark); 907 // commented out because ParsePosition doesn't have error index in 1.1.x 908 // restored for ICU4C port 909 if (highWaterMark > 0) { 910 parsePosition.setErrorIndex(0); 911 } 912 913 // this is a hack for one unusual condition: Normally, whether this 914 // rule belong to a fraction rule set or not is handled by its 915 // substitutions. But if that rule HAS NO substitutions, then 916 // we have to account for it here. By definition, if the matching 917 // rule in a fraction rule set has no substitutions, its numerator 918 // is 1, and so the result is the reciprocal of its base value. 919 if (isFractionRule && 920 highWaterMark > 0 && 921 sub1->isNullSubstitution()) { 922 result = 1 / result; 923 } 924 925 resVal.setDouble(result); 926 return TRUE; // ??? do we need to worry if it is a long or a double? 927 } 928 929 /** 930 * This function is used by parse() to match the text being parsed 931 * against a possible prefix string. This function 932 * matches characters from the beginning of the string being parsed 933 * to characters from the prospective prefix. If they match, pp is 934 * updated to the first character not matched, and the result is 935 * the unparsed part of the string. If they don't match, the whole 936 * string is returned, and pp is left unchanged. 937 * @param text The string being parsed 938 * @param prefix The text to match against 939 * @param pp On entry, ignored and assumed to be 0. On exit, points 940 * to the first unmatched character (assuming the whole prefix matched), 941 * or is unchanged (if the whole prefix didn't match). 942 * @return If things match, this is the unparsed part of "text"; 943 * if they didn't match, this is "text". 944 */ 945 void 946 NFRule::stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const 947 { 948 // if the prefix text is empty, dump out without doing anything 949 if (prefix.length() != 0) { 950 UErrorCode status = U_ZERO_ERROR; 951 // use prefixLength() to match the beginning of 952 // "text" against "prefix". This function returns the 953 // number of characters from "text" that matched (or 0 if 954 // we didn't match the whole prefix) 955 int32_t pfl = prefixLength(text, prefix, status); 956 if (U_FAILURE(status)) { // Memory allocation error. 957 return; 958 } 959 if (pfl != 0) { 960 // if we got a successful match, update the parse position 961 // and strip the prefix off of "text" 962 pp.setIndex(pp.getIndex() + pfl); 963 text.remove(0, pfl); 964 } 965 } 966 } 967 968 /** 969 * Used by parse() to match a substitution and any following text. 970 * "text" is searched for instances of "delimiter". For each instance 971 * of delimiter, the intervening text is tested to see whether it 972 * matches the substitution. The longest match wins. 973 * @param text The string being parsed 974 * @param startPos The position in "text" where we should start looking 975 * for "delimiter". 976 * @param baseValue A partial parse result (often the rule's base value), 977 * which is combined with the result from matching the substitution 978 * @param delimiter The string to search "text" for. 979 * @param pp Ignored and presumed to be 0 on entry. If there's a match, 980 * on exit this will point to the first unmatched character. 981 * @param sub If we find "delimiter" in "text", this substitution is used 982 * to match the text between the beginning of the string and the 983 * position of "delimiter." (If "delimiter" is the empty string, then 984 * this function just matches against this substitution and updates 985 * everything accordingly.) 986 * @param upperBound When matching the substitution, it will only 987 * consider rules with base values lower than this value. 988 * @return If there's a match, this is the result of composing 989 * baseValue with the result of matching the substitution. Otherwise, 990 * this is new Long(0). It's never null. If the result is an integer, 991 * this will be an instance of Long; otherwise, it's an instance of 992 * Double. 993 * 994 * !!! note {dlf} in point of fact, in the java code the caller always converts 995 * the result to a double, so we might as well return one. 996 */ 997 double 998 NFRule::matchToDelimiter(const UnicodeString& text, 999 int32_t startPos, 1000 double _baseValue, 1001 const UnicodeString& delimiter, 1002 ParsePosition& pp, 1003 const NFSubstitution* sub, 1004 double upperBound) const 1005 { 1006 UErrorCode status = U_ZERO_ERROR; 1007 // if "delimiter" contains real (i.e., non-ignorable) text, search 1008 // it for "delimiter" beginning at "start". If that succeeds, then 1009 // use "sub"'s doParse() method to match the text before the 1010 // instance of "delimiter" we just found. 1011 if (!allIgnorable(delimiter, status)) { 1012 if (U_FAILURE(status)) { //Memory allocation error. 1013 return 0; 1014 } 1015 ParsePosition tempPP; 1016 Formattable result; 1017 1018 // use findText() to search for "delimiter". It returns a two- 1019 // element array: element 0 is the position of the match, and 1020 // element 1 is the number of characters that matched 1021 // "delimiter". 1022 int32_t dLen; 1023 int32_t dPos = findText(text, delimiter, startPos, &dLen); 1024 1025 // if findText() succeeded, isolate the text preceding the 1026 // match, and use "sub" to match that text 1027 while (dPos >= 0) { 1028 UnicodeString subText; 1029 subText.setTo(text, 0, dPos); 1030 if (subText.length() > 0) { 1031 UBool success = sub->doParse(subText, tempPP, _baseValue, upperBound, 1032 #if UCONFIG_NO_COLLATION 1033 FALSE, 1034 #else 1035 formatter->isLenient(), 1036 #endif 1037 result); 1038 1039 // if the substitution could match all the text up to 1040 // where we found "delimiter", then this function has 1041 // a successful match. Bump the caller's parse position 1042 // to point to the first character after the text 1043 // that matches "delimiter", and return the result 1044 // we got from parsing the substitution. 1045 if (success && tempPP.getIndex() == dPos) { 1046 pp.setIndex(dPos + dLen); 1047 return result.getDouble(); 1048 } 1049 // commented out because ParsePosition doesn't have error index in 1.1.x 1050 // restored for ICU4C port 1051 else { 1052 if (tempPP.getErrorIndex() > 0) { 1053 pp.setErrorIndex(tempPP.getErrorIndex()); 1054 } else { 1055 pp.setErrorIndex(tempPP.getIndex()); 1056 } 1057 } 1058 } 1059 1060 // if we didn't match the substitution, search for another 1061 // copy of "delimiter" in "text" and repeat the loop if 1062 // we find it 1063 tempPP.setIndex(0); 1064 dPos = findText(text, delimiter, dPos + dLen, &dLen); 1065 } 1066 // if we make it here, this was an unsuccessful match, and we 1067 // leave pp unchanged and return 0 1068 pp.setIndex(0); 1069 return 0; 1070 1071 // if "delimiter" is empty, or consists only of ignorable characters 1072 // (i.e., is semantically empty), thwe we obviously can't search 1073 // for "delimiter". Instead, just use "sub" to parse as much of 1074 // "text" as possible. 1075 } else { 1076 ParsePosition tempPP; 1077 Formattable result; 1078 1079 // try to match the whole string against the substitution 1080 UBool success = sub->doParse(text, tempPP, _baseValue, upperBound, 1081 #if UCONFIG_NO_COLLATION 1082 FALSE, 1083 #else 1084 formatter->isLenient(), 1085 #endif 1086 result); 1087 if (success && (tempPP.getIndex() != 0 || sub->isNullSubstitution())) { 1088 // if there's a successful match (or it's a null 1089 // substitution), update pp to point to the first 1090 // character we didn't match, and pass the result from 1091 // sub.doParse() on through to the caller 1092 pp.setIndex(tempPP.getIndex()); 1093 return result.getDouble(); 1094 } 1095 // commented out because ParsePosition doesn't have error index in 1.1.x 1096 // restored for ICU4C port 1097 else { 1098 pp.setErrorIndex(tempPP.getErrorIndex()); 1099 } 1100 1101 // and if we get to here, then nothing matched, so we return 1102 // 0 and leave pp alone 1103 return 0; 1104 } 1105 } 1106 1107 /** 1108 * Used by stripPrefix() to match characters. If lenient parse mode 1109 * is off, this just calls startsWith(). If lenient parse mode is on, 1110 * this function uses CollationElementIterators to match characters in 1111 * the strings (only primary-order differences are significant in 1112 * determining whether there's a match). 1113 * @param str The string being tested 1114 * @param prefix The text we're hoping to see at the beginning 1115 * of "str" 1116 * @return If "prefix" is found at the beginning of "str", this 1117 * is the number of characters in "str" that were matched (this 1118 * isn't necessarily the same as the length of "prefix" when matching 1119 * text with a collator). If there's no match, this is 0. 1120 */ 1121 int32_t 1122 NFRule::prefixLength(const UnicodeString& str, const UnicodeString& prefix, UErrorCode& status) const 1123 { 1124 // if we're looking for an empty prefix, it obviously matches 1125 // zero characters. Just go ahead and return 0. 1126 if (prefix.length() == 0) { 1127 return 0; 1128 } 1129 1130 #if !UCONFIG_NO_COLLATION 1131 // go through all this grief if we're in lenient-parse mode 1132 if (formatter->isLenient()) { 1133 // get the formatter's collator and use it to create two 1134 // collation element iterators, one over the target string 1135 // and another over the prefix (right now, we'll throw an 1136 // exception if the collator we get back from the formatter 1137 // isn't a RuleBasedCollator, because RuleBasedCollator defines 1138 // the CollationElementIterator protocol. Hopefully, this 1139 // will change someday.) 1140 const RuleBasedCollator* collator = formatter->getCollator(); 1141 if (collator == NULL) { 1142 status = U_MEMORY_ALLOCATION_ERROR; 1143 return 0; 1144 } 1145 LocalPointer<CollationElementIterator> strIter(collator->createCollationElementIterator(str)); 1146 LocalPointer<CollationElementIterator> prefixIter(collator->createCollationElementIterator(prefix)); 1147 // Check for memory allocation error. 1148 if (strIter.isNull() || prefixIter.isNull()) { 1149 status = U_MEMORY_ALLOCATION_ERROR; 1150 return 0; 1151 } 1152 1153 UErrorCode err = U_ZERO_ERROR; 1154 1155 // The original code was problematic. Consider this match: 1156 // prefix = "fifty-" 1157 // string = " fifty-7" 1158 // The intent is to match string up to the '7', by matching 'fifty-' at position 1 1159 // in the string. Unfortunately, we were getting a match, and then computing where 1160 // the match terminated by rematching the string. The rematch code was using as an 1161 // initial guess the substring of string between 0 and prefix.length. Because of 1162 // the leading space and trailing hyphen (both ignorable) this was succeeding, leaving 1163 // the position before the hyphen in the string. Recursing down, we then parsed the 1164 // remaining string '-7' as numeric. The resulting number turned out as 43 (50 - 7). 1165 // This was not pretty, especially since the string "fifty-7" parsed just fine. 1166 // 1167 // We have newer APIs now, so we can use calls on the iterator to determine what we 1168 // matched up to. If we terminate because we hit the last element in the string, 1169 // our match terminates at this length. If we terminate because we hit the last element 1170 // in the target, our match terminates at one before the element iterator position. 1171 1172 // match collation elements between the strings 1173 int32_t oStr = strIter->next(err); 1174 int32_t oPrefix = prefixIter->next(err); 1175 1176 while (oPrefix != CollationElementIterator::NULLORDER) { 1177 // skip over ignorable characters in the target string 1178 while (CollationElementIterator::primaryOrder(oStr) == 0 1179 && oStr != CollationElementIterator::NULLORDER) { 1180 oStr = strIter->next(err); 1181 } 1182 1183 // skip over ignorable characters in the prefix 1184 while (CollationElementIterator::primaryOrder(oPrefix) == 0 1185 && oPrefix != CollationElementIterator::NULLORDER) { 1186 oPrefix = prefixIter->next(err); 1187 } 1188 1189 // dlf: move this above following test, if we consume the 1190 // entire target, aren't we ok even if the source was also 1191 // entirely consumed? 1192 1193 // if skipping over ignorables brought to the end of 1194 // the prefix, we DID match: drop out of the loop 1195 if (oPrefix == CollationElementIterator::NULLORDER) { 1196 break; 1197 } 1198 1199 // if skipping over ignorables brought us to the end 1200 // of the target string, we didn't match and return 0 1201 if (oStr == CollationElementIterator::NULLORDER) { 1202 return 0; 1203 } 1204 1205 // match collation elements from the two strings 1206 // (considering only primary differences). If we 1207 // get a mismatch, dump out and return 0 1208 if (CollationElementIterator::primaryOrder(oStr) 1209 != CollationElementIterator::primaryOrder(oPrefix)) { 1210 return 0; 1211 1212 // otherwise, advance to the next character in each string 1213 // and loop (we drop out of the loop when we exhaust 1214 // collation elements in the prefix) 1215 } else { 1216 oStr = strIter->next(err); 1217 oPrefix = prefixIter->next(err); 1218 } 1219 } 1220 1221 int32_t result = strIter->getOffset(); 1222 if (oStr != CollationElementIterator::NULLORDER) { 1223 --result; // back over character that we don't want to consume; 1224 } 1225 1226 #ifdef RBNF_DEBUG 1227 fprintf(stderr, "prefix length: %d\n", result); 1228 #endif 1229 return result; 1230 #if 0 1231 //---------------------------------------------------------------- 1232 // JDK 1.2-specific API call 1233 // return strIter.getOffset(); 1234 //---------------------------------------------------------------- 1235 // JDK 1.1 HACK (take out for 1.2-specific code) 1236 1237 // if we make it to here, we have a successful match. Now we 1238 // have to find out HOW MANY characters from the target string 1239 // matched the prefix (there isn't necessarily a one-to-one 1240 // mapping between collation elements and characters). 1241 // In JDK 1.2, there's a simple getOffset() call we can use. 1242 // In JDK 1.1, on the other hand, we have to go through some 1243 // ugly contortions. First, use the collator to compare the 1244 // same number of characters from the prefix and target string. 1245 // If they're equal, we're done. 1246 collator->setStrength(Collator::PRIMARY); 1247 if (str.length() >= prefix.length()) { 1248 UnicodeString temp; 1249 temp.setTo(str, 0, prefix.length()); 1250 if (collator->equals(temp, prefix)) { 1251 #ifdef RBNF_DEBUG 1252 fprintf(stderr, "returning: %d\n", prefix.length()); 1253 #endif 1254 return prefix.length(); 1255 } 1256 } 1257 1258 // if they're not equal, then we have to compare successively 1259 // larger and larger substrings of the target string until we 1260 // get to one that matches the prefix. At that point, we know 1261 // how many characters matched the prefix, and we can return. 1262 int32_t p = 1; 1263 while (p <= str.length()) { 1264 UnicodeString temp; 1265 temp.setTo(str, 0, p); 1266 if (collator->equals(temp, prefix)) { 1267 return p; 1268 } else { 1269 ++p; 1270 } 1271 } 1272 1273 // SHOULD NEVER GET HERE!!! 1274 return 0; 1275 //---------------------------------------------------------------- 1276 #endif 1277 1278 // If lenient parsing is turned off, forget all that crap above. 1279 // Just use String.startsWith() and be done with it. 1280 } else 1281 #endif 1282 { 1283 if (str.startsWith(prefix)) { 1284 return prefix.length(); 1285 } else { 1286 return 0; 1287 } 1288 } 1289 } 1290 1291 /** 1292 * Searches a string for another string. If lenient parsing is off, 1293 * this just calls indexOf(). If lenient parsing is on, this function 1294 * uses CollationElementIterator to match characters, and only 1295 * primary-order differences are significant in determining whether 1296 * there's a match. 1297 * @param str The string to search 1298 * @param key The string to search "str" for 1299 * @param startingAt The index into "str" where the search is to 1300 * begin 1301 * @return A two-element array of ints. Element 0 is the position 1302 * of the match, or -1 if there was no match. Element 1 is the 1303 * number of characters in "str" that matched (which isn't necessarily 1304 * the same as the length of "key") 1305 */ 1306 int32_t 1307 NFRule::findText(const UnicodeString& str, 1308 const UnicodeString& key, 1309 int32_t startingAt, 1310 int32_t* length) const 1311 { 1312 #if !UCONFIG_NO_COLLATION 1313 // if lenient parsing is turned off, this is easy: just call 1314 // String.indexOf() and we're done 1315 if (!formatter->isLenient()) { 1316 *length = key.length(); 1317 return str.indexOf(key, startingAt); 1318 1319 // but if lenient parsing is turned ON, we've got some work 1320 // ahead of us 1321 } else 1322 #endif 1323 { 1324 //---------------------------------------------------------------- 1325 // JDK 1.1 HACK (take out of 1.2-specific code) 1326 1327 // in JDK 1.2, CollationElementIterator provides us with an 1328 // API to map between character offsets and collation elements 1329 // and we can do this by marching through the string comparing 1330 // collation elements. We can't do that in JDK 1.1. Insted, 1331 // we have to go through this horrible slow mess: 1332 int32_t p = startingAt; 1333 int32_t keyLen = 0; 1334 1335 // basically just isolate smaller and smaller substrings of 1336 // the target string (each running to the end of the string, 1337 // and with the first one running from startingAt to the end) 1338 // and then use prefixLength() to see if the search key is at 1339 // the beginning of each substring. This is excruciatingly 1340 // slow, but it will locate the key and tell use how long the 1341 // matching text was. 1342 UnicodeString temp; 1343 UErrorCode status = U_ZERO_ERROR; 1344 while (p < str.length() && keyLen == 0) { 1345 temp.setTo(str, p, str.length() - p); 1346 keyLen = prefixLength(temp, key, status); 1347 if (U_FAILURE(status)) { 1348 break; 1349 } 1350 if (keyLen != 0) { 1351 *length = keyLen; 1352 return p; 1353 } 1354 ++p; 1355 } 1356 // if we make it to here, we didn't find it. Return -1 for the 1357 // location. The length should be ignored, but set it to 0, 1358 // which should be "safe" 1359 *length = 0; 1360 return -1; 1361 1362 //---------------------------------------------------------------- 1363 // JDK 1.2 version of this routine 1364 //RuleBasedCollator collator = (RuleBasedCollator)formatter.getCollator(); 1365 // 1366 //CollationElementIterator strIter = collator.getCollationElementIterator(str); 1367 //CollationElementIterator keyIter = collator.getCollationElementIterator(key); 1368 // 1369 //int keyStart = -1; 1370 // 1371 //str.setOffset(startingAt); 1372 // 1373 //int oStr = strIter.next(); 1374 //int oKey = keyIter.next(); 1375 //while (oKey != CollationElementIterator.NULLORDER) { 1376 // while (oStr != CollationElementIterator.NULLORDER && 1377 // CollationElementIterator.primaryOrder(oStr) == 0) 1378 // oStr = strIter.next(); 1379 // 1380 // while (oKey != CollationElementIterator.NULLORDER && 1381 // CollationElementIterator.primaryOrder(oKey) == 0) 1382 // oKey = keyIter.next(); 1383 // 1384 // if (oStr == CollationElementIterator.NULLORDER) { 1385 // return new int[] { -1, 0 }; 1386 // } 1387 // 1388 // if (oKey == CollationElementIterator.NULLORDER) { 1389 // break; 1390 // } 1391 // 1392 // if (CollationElementIterator.primaryOrder(oStr) == 1393 // CollationElementIterator.primaryOrder(oKey)) { 1394 // keyStart = strIter.getOffset(); 1395 // oStr = strIter.next(); 1396 // oKey = keyIter.next(); 1397 // } else { 1398 // if (keyStart != -1) { 1399 // keyStart = -1; 1400 // keyIter.reset(); 1401 // } else { 1402 // oStr = strIter.next(); 1403 // } 1404 // } 1405 //} 1406 // 1407 //if (oKey == CollationElementIterator.NULLORDER) { 1408 // return new int[] { keyStart, strIter.getOffset() - keyStart }; 1409 //} else { 1410 // return new int[] { -1, 0 }; 1411 //} 1412 } 1413 } 1414 1415 /** 1416 * Checks to see whether a string consists entirely of ignorable 1417 * characters. 1418 * @param str The string to test. 1419 * @return true if the string is empty of consists entirely of 1420 * characters that the number formatter's collator says are 1421 * ignorable at the primary-order level. false otherwise. 1422 */ 1423 UBool 1424 NFRule::allIgnorable(const UnicodeString& str, UErrorCode& status) const 1425 { 1426 // if the string is empty, we can just return true 1427 if (str.length() == 0) { 1428 return TRUE; 1429 } 1430 1431 #if !UCONFIG_NO_COLLATION 1432 // if lenient parsing is turned on, walk through the string with 1433 // a collation element iterator and make sure each collation 1434 // element is 0 (ignorable) at the primary level 1435 if (formatter->isLenient()) { 1436 const RuleBasedCollator* collator = formatter->getCollator(); 1437 if (collator == NULL) { 1438 status = U_MEMORY_ALLOCATION_ERROR; 1439 return FALSE; 1440 } 1441 LocalPointer<CollationElementIterator> iter(collator->createCollationElementIterator(str)); 1442 1443 // Memory allocation error check. 1444 if (iter.isNull()) { 1445 status = U_MEMORY_ALLOCATION_ERROR; 1446 return FALSE; 1447 } 1448 1449 UErrorCode err = U_ZERO_ERROR; 1450 int32_t o = iter->next(err); 1451 while (o != CollationElementIterator::NULLORDER 1452 && CollationElementIterator::primaryOrder(o) == 0) { 1453 o = iter->next(err); 1454 } 1455 1456 return o == CollationElementIterator::NULLORDER; 1457 } 1458 #endif 1459 1460 // if lenient parsing is turned off, there is no such thing as 1461 // an ignorable character: return true only if the string is empty 1462 return FALSE; 1463 } 1464 1465 U_NAMESPACE_END 1466 1467 /* U_HAVE_RBNF */ 1468 #endif 1469