LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - nfrule.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 515 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13