Home | History | Annotate | Download | only in i18n
      1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 2007-2016, International Business Machines Corporation and
      6 * others. All Rights Reserved.
      7 *******************************************************************************
      8 *
      9 * File plurrule.cpp
     10 */
     11 
     12 #include <math.h>
     13 #include <stdio.h>
     14 
     15 #include "unicode/utypes.h"
     16 #include "unicode/localpointer.h"
     17 #include "unicode/plurrule.h"
     18 #include "unicode/upluralrules.h"
     19 #include "unicode/ures.h"
     20 #include "charstr.h"
     21 #include "cmemory.h"
     22 #include "cstring.h"
     23 #include "digitlst.h"
     24 #include "hash.h"
     25 #include "locutil.h"
     26 #include "mutex.h"
     27 #include "patternprops.h"
     28 #include "plurrule_impl.h"
     29 #include "putilimp.h"
     30 #include "ucln_in.h"
     31 #include "ustrfmt.h"
     32 #include "uassert.h"
     33 #include "uvectr32.h"
     34 #include "sharedpluralrules.h"
     35 #include "unifiedcache.h"
     36 #include "digitinterval.h"
     37 #include "visibledigits.h"
     38 
     39 
     40 #if !UCONFIG_NO_FORMATTING
     41 
     42 U_NAMESPACE_BEGIN
     43 
     44 static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0};
     45 static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0};
     46 static const UChar PK_IN[]={LOW_I,LOW_N,0};
     47 static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0};
     48 static const UChar PK_IS[]={LOW_I,LOW_S,0};
     49 static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0};
     50 static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0};
     51 static const UChar PK_OR[]={LOW_O,LOW_R,0};
     52 static const UChar PK_VAR_N[]={LOW_N,0};
     53 static const UChar PK_VAR_I[]={LOW_I,0};
     54 static const UChar PK_VAR_F[]={LOW_F,0};
     55 static const UChar PK_VAR_T[]={LOW_T,0};
     56 static const UChar PK_VAR_V[]={LOW_V,0};
     57 static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
     58 static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0};
     59 static const UChar PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0};
     60 
     61 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules)
     62 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
     63 
     64 PluralRules::PluralRules(UErrorCode& /*status*/)
     65 :   UObject(),
     66     mRules(NULL)
     67 {
     68 }
     69 
     70 PluralRules::PluralRules(const PluralRules& other)
     71 : UObject(other),
     72     mRules(NULL)
     73 {
     74     *this=other;
     75 }
     76 
     77 PluralRules::~PluralRules() {
     78     delete mRules;
     79 }
     80 
     81 SharedPluralRules::~SharedPluralRules() {
     82     delete ptr;
     83 }
     84 
     85 PluralRules*
     86 PluralRules::clone() const {
     87     return new PluralRules(*this);
     88 }
     89 
     90 PluralRules&
     91 PluralRules::operator=(const PluralRules& other) {
     92     if (this != &other) {
     93         delete mRules;
     94         if (other.mRules==NULL) {
     95             mRules = NULL;
     96         }
     97         else {
     98             mRules = new RuleChain(*other.mRules);
     99         }
    100     }
    101 
    102     return *this;
    103 }
    104 
    105 StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) {
    106     StringEnumeration *result = new PluralAvailableLocalesEnumeration(status);
    107     if (result == NULL && U_SUCCESS(status)) {
    108         status = U_MEMORY_ALLOCATION_ERROR;
    109     }
    110     if (U_FAILURE(status)) {
    111         delete result;
    112         result = NULL;
    113     }
    114     return result;
    115 }
    116 
    117 
    118 PluralRules* U_EXPORT2
    119 PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
    120     if (U_FAILURE(status)) {
    121         return NULL;
    122     }
    123 
    124     PluralRuleParser parser;
    125     PluralRules *newRules = new PluralRules(status);
    126     if (U_SUCCESS(status) && newRules == NULL) {
    127         status = U_MEMORY_ALLOCATION_ERROR;
    128     }
    129     parser.parse(description, newRules, status);
    130     if (U_FAILURE(status)) {
    131         delete newRules;
    132         newRules = NULL;
    133     }
    134     return newRules;
    135 }
    136 
    137 
    138 PluralRules* U_EXPORT2
    139 PluralRules::createDefaultRules(UErrorCode& status) {
    140     return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status);
    141 }
    142 
    143 /******************************************************************************/
    144 /* Create PluralRules cache */
    145 
    146 template<> U_I18N_API
    147 const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject(
    148         const void * /*unused*/, UErrorCode &status) const {
    149     const char *localeId = fLoc.getName();
    150     PluralRules *pr = PluralRules::internalForLocale(
    151             localeId, UPLURAL_TYPE_CARDINAL, status);
    152     if (U_FAILURE(status)) {
    153         return NULL;
    154     }
    155     SharedPluralRules *result = new SharedPluralRules(pr);
    156     if (result == NULL) {
    157         status = U_MEMORY_ALLOCATION_ERROR;
    158         delete pr;
    159         return NULL;
    160     }
    161     result->addRef();
    162     return result;
    163 }
    164 
    165 /* end plural rules cache */
    166 /******************************************************************************/
    167 
    168 const SharedPluralRules* U_EXPORT2
    169 PluralRules::createSharedInstance(
    170         const Locale& locale, UPluralType type, UErrorCode& status) {
    171     if (U_FAILURE(status)) {
    172         return NULL;
    173     }
    174     if (type != UPLURAL_TYPE_CARDINAL) {
    175         status = U_UNSUPPORTED_ERROR;
    176         return NULL;
    177     }
    178     const SharedPluralRules *result = NULL;
    179     UnifiedCache::getByLocale(locale, result, status);
    180     return result;
    181 }
    182 
    183 PluralRules* U_EXPORT2
    184 PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
    185     return forLocale(locale, UPLURAL_TYPE_CARDINAL, status);
    186 }
    187 
    188 PluralRules* U_EXPORT2
    189 PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
    190     if (type != UPLURAL_TYPE_CARDINAL) {
    191         return internalForLocale(locale, type, status);
    192     }
    193     const SharedPluralRules *shared = createSharedInstance(
    194             locale, type, status);
    195     if (U_FAILURE(status)) {
    196         return NULL;
    197     }
    198     PluralRules *result = (*shared)->clone();
    199     shared->removeRef();
    200     if (result == NULL) {
    201         status = U_MEMORY_ALLOCATION_ERROR;
    202     }
    203     return result;
    204 }
    205 
    206 PluralRules* U_EXPORT2
    207 PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
    208     if (U_FAILURE(status)) {
    209         return NULL;
    210     }
    211     if (type >= UPLURAL_TYPE_COUNT) {
    212         status = U_ILLEGAL_ARGUMENT_ERROR;
    213         return NULL;
    214     }
    215     PluralRules *newObj = new PluralRules(status);
    216     if (newObj==NULL || U_FAILURE(status)) {
    217         delete newObj;
    218         return NULL;
    219     }
    220     UnicodeString locRule = newObj->getRuleFromResource(locale, type, status);
    221     // TODO: which errors, if any, should be returned?
    222     if (locRule.length() == 0) {
    223         // Locales with no specific rules (all numbers have the "other" category
    224         //   will return a U_MISSING_RESOURCE_ERROR at this point. This is not
    225         //   an error.
    226         locRule =  UnicodeString(PLURAL_DEFAULT_RULE);
    227         status = U_ZERO_ERROR;
    228     }
    229     PluralRuleParser parser;
    230     parser.parse(locRule, newObj, status);
    231         //  TODO: should rule parse errors be returned, or
    232         //        should we silently use default rules?
    233         //        Original impl used default rules.
    234         //        Ask the question to ICU Core.
    235 
    236     return newObj;
    237 }
    238 
    239 UnicodeString
    240 PluralRules::select(int32_t number) const {
    241     return select(FixedDecimal(number));
    242 }
    243 
    244 UnicodeString
    245 PluralRules::select(double number) const {
    246     return select(FixedDecimal(number));
    247 }
    248 
    249 UnicodeString
    250 PluralRules::select(const FixedDecimal &number) const {
    251     if (mRules == NULL) {
    252         return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
    253     }
    254     else {
    255         return mRules->select(number);
    256     }
    257 }
    258 
    259 UnicodeString
    260 PluralRules::select(const VisibleDigitsWithExponent &number) const {
    261     if (number.getExponent() != NULL) {
    262         return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
    263     }
    264     return select(FixedDecimal(number.getMantissa()));
    265 }
    266 
    267 
    268 
    269 StringEnumeration*
    270 PluralRules::getKeywords(UErrorCode& status) const {
    271     if (U_FAILURE(status))  return NULL;
    272     StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status);
    273     if (U_FAILURE(status)) {
    274       delete nameEnumerator;
    275       return NULL;
    276     }
    277 
    278     return nameEnumerator;
    279 }
    280 
    281 double
    282 PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) {
    283   // Not Implemented.
    284   return UPLRULES_NO_UNIQUE_VALUE;
    285 }
    286 
    287 int32_t
    288 PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */,
    289                                  int32_t /* destCapacity */, UErrorCode& error) {
    290     error = U_UNSUPPORTED_ERROR;
    291     return 0;
    292 }
    293 
    294 
    295 static double scaleForInt(double d) {
    296     double scale = 1.0;
    297     while (d != floor(d)) {
    298         d = d * 10.0;
    299         scale = scale * 10.0;
    300     }
    301     return scale;
    302 }
    303 
    304 static int32_t
    305 getSamplesFromString(const UnicodeString &samples, double *dest,
    306                         int32_t destCapacity, UErrorCode& status) {
    307     int32_t sampleCount = 0;
    308     int32_t sampleStartIdx = 0;
    309     int32_t sampleEndIdx = 0;
    310 
    311     //std::string ss;  // TODO: debugging.
    312     // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n";
    313     for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) {
    314         sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx);
    315         if (sampleEndIdx == -1) {
    316             sampleEndIdx = samples.length();
    317         }
    318         const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx);
    319         // ss.erase();
    320         // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n";
    321         int32_t tildeIndex = sampleRange.indexOf(TILDE);
    322         if (tildeIndex < 0) {
    323             FixedDecimal fixed(sampleRange, status);
    324             double sampleValue = fixed.source;
    325             if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) {
    326                 dest[sampleCount++] = sampleValue;
    327             }
    328         } else {
    329 
    330             FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status);
    331             FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status);
    332             double rangeLo = fixedLo.source;
    333             double rangeHi = fixedHi.source;
    334             if (U_FAILURE(status)) {
    335                 break;
    336             }
    337             if (rangeHi < rangeLo) {
    338                 status = U_INVALID_FORMAT_ERROR;
    339                 break;
    340             }
    341 
    342             // For ranges of samples with fraction decimal digits, scale the number up so that we
    343             //   are adding one in the units place. Avoids roundoffs from repetitive adds of tenths.
    344 
    345             double scale = scaleForInt(rangeLo);
    346             double t = scaleForInt(rangeHi);
    347             if (t > scale) {
    348                 scale = t;
    349             }
    350             rangeLo *= scale;
    351             rangeHi *= scale;
    352             for (double n=rangeLo; n<=rangeHi; n+=1) {
    353                 // Hack Alert: don't return any decimal samples with integer values that
    354                 //    originated from a format with trailing decimals.
    355                 //    This API is returning doubles, which can't distinguish having displayed
    356                 //    zeros to the right of the decimal.
    357                 //    This results in test failures with values mapping back to a different keyword.
    358                 double sampleValue = n/scale;
    359                 if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) {
    360                     dest[sampleCount++] = sampleValue;
    361                 }
    362                 if (sampleCount >= destCapacity) {
    363                     break;
    364                 }
    365             }
    366         }
    367         sampleStartIdx = sampleEndIdx + 1;
    368     }
    369     return sampleCount;
    370 }
    371 
    372 
    373 int32_t
    374 PluralRules::getSamples(const UnicodeString &keyword, double *dest,
    375                         int32_t destCapacity, UErrorCode& status) {
    376     RuleChain *rc = rulesForKeyword(keyword);
    377     if (rc == NULL || destCapacity == 0 || U_FAILURE(status)) {
    378         return 0;
    379     }
    380     int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status);
    381     if (numSamples == 0) {
    382         numSamples = getSamplesFromString(rc->fDecimalSamples, dest, destCapacity, status);
    383     }
    384     return numSamples;
    385 }
    386 
    387 
    388 RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const {
    389     RuleChain *rc;
    390     for (rc = mRules; rc != NULL; rc = rc->fNext) {
    391         if (rc->fKeyword == keyword) {
    392             break;
    393         }
    394     }
    395     return rc;
    396 }
    397 
    398 
    399 UBool
    400 PluralRules::isKeyword(const UnicodeString& keyword) const {
    401     if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
    402         return true;
    403     }
    404     return rulesForKeyword(keyword) != NULL;
    405 }
    406 
    407 UnicodeString
    408 PluralRules::getKeywordOther() const {
    409     return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5);
    410 }
    411 
    412 UBool
    413 PluralRules::operator==(const PluralRules& other) const  {
    414     const UnicodeString *ptrKeyword;
    415     UErrorCode status= U_ZERO_ERROR;
    416 
    417     if ( this == &other ) {
    418         return TRUE;
    419     }
    420     LocalPointer<StringEnumeration> myKeywordList(getKeywords(status));
    421     LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status));
    422     if (U_FAILURE(status)) {
    423         return FALSE;
    424     }
    425 
    426     if (myKeywordList->count(status)!=otherKeywordList->count(status)) {
    427         return FALSE;
    428     }
    429     myKeywordList->reset(status);
    430     while ((ptrKeyword=myKeywordList->snext(status))!=NULL) {
    431         if (!other.isKeyword(*ptrKeyword)) {
    432             return FALSE;
    433         }
    434     }
    435     otherKeywordList->reset(status);
    436     while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) {
    437         if (!this->isKeyword(*ptrKeyword)) {
    438             return FALSE;
    439         }
    440     }
    441     if (U_FAILURE(status)) {
    442         return FALSE;
    443     }
    444 
    445     return TRUE;
    446 }
    447 
    448 
    449 void
    450 PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status)
    451 {
    452     if (U_FAILURE(status)) {
    453         return;
    454     }
    455     U_ASSERT(ruleIndex == 0);    // Parsers are good for a single use only!
    456     ruleSrc = &ruleData;
    457 
    458     while (ruleIndex< ruleSrc->length()) {
    459         getNextToken(status);
    460         if (U_FAILURE(status)) {
    461             return;
    462         }
    463         checkSyntax(status);
    464         if (U_FAILURE(status)) {
    465             return;
    466         }
    467         switch (type) {
    468         case tAnd:
    469             U_ASSERT(curAndConstraint != NULL);
    470             curAndConstraint = curAndConstraint->add();
    471             break;
    472         case tOr:
    473             {
    474                 U_ASSERT(currentChain != NULL);
    475                 OrConstraint *orNode=currentChain->ruleHeader;
    476                 while (orNode->next != NULL) {
    477                     orNode = orNode->next;
    478                 }
    479                 orNode->next= new OrConstraint();
    480                 orNode=orNode->next;
    481                 orNode->next=NULL;
    482                 curAndConstraint = orNode->add();
    483             }
    484             break;
    485         case tIs:
    486             U_ASSERT(curAndConstraint != NULL);
    487             U_ASSERT(curAndConstraint->value == -1);
    488             U_ASSERT(curAndConstraint->rangeList == NULL);
    489             break;
    490         case tNot:
    491             U_ASSERT(curAndConstraint != NULL);
    492             curAndConstraint->negated=TRUE;
    493             break;
    494 
    495         case tNotEqual:
    496             curAndConstraint->negated=TRUE;
    497             U_FALLTHROUGH;
    498         case tIn:
    499         case tWithin:
    500         case tEqual:
    501             U_ASSERT(curAndConstraint != NULL);
    502             curAndConstraint->rangeList = new UVector32(status);
    503             curAndConstraint->rangeList->addElement(-1, status);  // range Low
    504             curAndConstraint->rangeList->addElement(-1, status);  // range Hi
    505             rangeLowIdx = 0;
    506             rangeHiIdx  = 1;
    507             curAndConstraint->value=PLURAL_RANGE_HIGH;
    508             curAndConstraint->integerOnly = (type != tWithin);
    509             break;
    510         case tNumber:
    511             U_ASSERT(curAndConstraint != NULL);
    512             if ( (curAndConstraint->op==AndConstraint::MOD)&&
    513                  (curAndConstraint->opNum == -1 ) ) {
    514                 curAndConstraint->opNum=getNumberValue(token);
    515             }
    516             else {
    517                 if (curAndConstraint->rangeList == NULL) {
    518                     // this is for an 'is' rule
    519                     curAndConstraint->value = getNumberValue(token);
    520                 } else {
    521                     // this is for an 'in' or 'within' rule
    522                     if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) {
    523                         curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx);
    524                         curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx);
    525                     }
    526                     else {
    527                         curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx);
    528                         if (curAndConstraint->rangeList->elementAti(rangeLowIdx) >
    529                                 curAndConstraint->rangeList->elementAti(rangeHiIdx)) {
    530                             // Range Lower bound > Range Upper bound.
    531                             // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently
    532                             // used for all plural rule parse errors.
    533                             status = U_UNEXPECTED_TOKEN;
    534                             break;
    535                         }
    536                     }
    537                 }
    538             }
    539             break;
    540         case tComma:
    541             // TODO: rule syntax checking is inadequate, can happen with badly formed rules.
    542             //       Catch cases like "n mod 10, is 1" here instead.
    543             if (curAndConstraint == NULL || curAndConstraint->rangeList == NULL) {
    544                 status = U_UNEXPECTED_TOKEN;
    545                 break;
    546             }
    547             U_ASSERT(curAndConstraint->rangeList->size() >= 2);
    548             rangeLowIdx = curAndConstraint->rangeList->size();
    549             curAndConstraint->rangeList->addElement(-1, status);  // range Low
    550             rangeHiIdx = curAndConstraint->rangeList->size();
    551             curAndConstraint->rangeList->addElement(-1, status);  // range Hi
    552             break;
    553         case tMod:
    554             U_ASSERT(curAndConstraint != NULL);
    555             curAndConstraint->op=AndConstraint::MOD;
    556             break;
    557         case tVariableN:
    558         case tVariableI:
    559         case tVariableF:
    560         case tVariableT:
    561         case tVariableV:
    562             U_ASSERT(curAndConstraint != NULL);
    563             curAndConstraint->digitsType = type;
    564             break;
    565         case tKeyword:
    566             {
    567             RuleChain *newChain = new RuleChain;
    568             if (newChain == NULL) {
    569                 status = U_MEMORY_ALLOCATION_ERROR;
    570                 break;
    571             }
    572             newChain->fKeyword = token;
    573             if (prules->mRules == NULL) {
    574                 prules->mRules = newChain;
    575             } else {
    576                 // The new rule chain goes at the end of the linked list of rule chains,
    577                 //   unless there is an "other" keyword & chain. "other" must remain last.
    578                 RuleChain *insertAfter = prules->mRules;
    579                 while (insertAfter->fNext!=NULL &&
    580                        insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){
    581                     insertAfter=insertAfter->fNext;
    582                 }
    583                 newChain->fNext = insertAfter->fNext;
    584                 insertAfter->fNext = newChain;
    585             }
    586             OrConstraint *orNode = new OrConstraint();
    587             newChain->ruleHeader = orNode;
    588             curAndConstraint = orNode->add();
    589             currentChain = newChain;
    590             }
    591             break;
    592 
    593         case tInteger:
    594             for (;;) {
    595                 getNextToken(status);
    596                 if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) {
    597                     break;
    598                 }
    599                 if (type == tEllipsis) {
    600                     currentChain->fIntegerSamplesUnbounded = TRUE;
    601                     continue;
    602                 }
    603                 currentChain->fIntegerSamples.append(token);
    604             }
    605             break;
    606 
    607         case tDecimal:
    608             for (;;) {
    609                 getNextToken(status);
    610                 if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) {
    611                     break;
    612                 }
    613                 if (type == tEllipsis) {
    614                     currentChain->fDecimalSamplesUnbounded = TRUE;
    615                     continue;
    616                 }
    617                 currentChain->fDecimalSamples.append(token);
    618             }
    619             break;
    620 
    621         default:
    622             break;
    623         }
    624         prevType=type;
    625         if (U_FAILURE(status)) {
    626             break;
    627         }
    628     }
    629 }
    630 
    631 UnicodeString
    632 PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) {
    633     UnicodeString emptyStr;
    634 
    635     if (U_FAILURE(errCode)) {
    636         return emptyStr;
    637     }
    638     LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &errCode));
    639     if(U_FAILURE(errCode)) {
    640         return emptyStr;
    641     }
    642     const char *typeKey;
    643     switch (type) {
    644     case UPLURAL_TYPE_CARDINAL:
    645         typeKey = "locales";
    646         break;
    647     case UPLURAL_TYPE_ORDINAL:
    648         typeKey = "locales_ordinals";
    649         break;
    650     default:
    651         // Must not occur: The caller should have checked for valid types.
    652         errCode = U_ILLEGAL_ARGUMENT_ERROR;
    653         return emptyStr;
    654     }
    655     LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, NULL, &errCode));
    656     if(U_FAILURE(errCode)) {
    657         return emptyStr;
    658     }
    659     int32_t resLen=0;
    660     const char *curLocaleName=locale.getName();
    661     const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode);
    662 
    663     if (s == NULL) {
    664         // Check parent locales.
    665         UErrorCode status = U_ZERO_ERROR;
    666         char parentLocaleName[ULOC_FULLNAME_CAPACITY];
    667         const char *curLocaleName=locale.getName();
    668         uprv_strcpy(parentLocaleName, curLocaleName);
    669 
    670         while (uloc_getParent(parentLocaleName, parentLocaleName,
    671                                        ULOC_FULLNAME_CAPACITY, &status) > 0) {
    672             resLen=0;
    673             s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status);
    674             if (s != NULL) {
    675                 errCode = U_ZERO_ERROR;
    676                 break;
    677             }
    678             status = U_ZERO_ERROR;
    679         }
    680     }
    681     if (s==NULL) {
    682         return emptyStr;
    683     }
    684 
    685     char setKey[256];
    686     u_UCharsToChars(s, setKey, resLen + 1);
    687     // printf("\n PluralRule: %s\n", setKey);
    688 
    689     LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", NULL, &errCode));
    690     if(U_FAILURE(errCode)) {
    691         return emptyStr;
    692     }
    693     LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, NULL, &errCode));
    694     if (U_FAILURE(errCode)) {
    695         return emptyStr;
    696     }
    697 
    698     int32_t numberKeys = ures_getSize(setRes.getAlias());
    699     UnicodeString result;
    700     const char *key=NULL;
    701     for(int32_t i=0; i<numberKeys; ++i) {   // Keys are zero, one, few, ...
    702         UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode);
    703         UnicodeString uKey(key, -1, US_INV);
    704         result.append(uKey);
    705         result.append(COLON);
    706         result.append(rules);
    707         result.append(SEMI_COLON);
    708     }
    709     return result;
    710 }
    711 
    712 
    713 UnicodeString
    714 PluralRules::getRules() const {
    715     UnicodeString rules;
    716     if (mRules != NULL) {
    717         mRules->dumpRules(rules);
    718     }
    719     return rules;
    720 }
    721 
    722 
    723 AndConstraint::AndConstraint() {
    724     op = AndConstraint::NONE;
    725     opNum=-1;
    726     value = -1;
    727     rangeList = NULL;
    728     negated = FALSE;
    729     integerOnly = FALSE;
    730     digitsType = none;
    731     next=NULL;
    732 }
    733 
    734 
    735 AndConstraint::AndConstraint(const AndConstraint& other) {
    736     this->op = other.op;
    737     this->opNum=other.opNum;
    738     this->value=other.value;
    739     this->rangeList=NULL;
    740     if (other.rangeList != NULL) {
    741         UErrorCode status = U_ZERO_ERROR;
    742         this->rangeList = new UVector32(status);
    743         this->rangeList->assign(*other.rangeList, status);
    744     }
    745     this->integerOnly=other.integerOnly;
    746     this->negated=other.negated;
    747     this->digitsType = other.digitsType;
    748     if (other.next==NULL) {
    749         this->next=NULL;
    750     }
    751     else {
    752         this->next = new AndConstraint(*other.next);
    753     }
    754 }
    755 
    756 AndConstraint::~AndConstraint() {
    757     delete rangeList;
    758     if (next!=NULL) {
    759         delete next;
    760     }
    761 }
    762 
    763 
    764 UBool
    765 AndConstraint::isFulfilled(const FixedDecimal &number) {
    766     UBool result = TRUE;
    767     if (digitsType == none) {
    768         // An empty AndConstraint, created by a rule with a keyword but no following expression.
    769         return TRUE;
    770     }
    771     double n = number.get(digitsType);  // pulls n | i | v | f value for the number.
    772                                         // Will always be positive.
    773                                         // May be non-integer (n option only)
    774     do {
    775         if (integerOnly && n != uprv_floor(n)) {
    776             result = FALSE;
    777             break;
    778         }
    779 
    780         if (op == MOD) {
    781             n = fmod(n, opNum);
    782         }
    783         if (rangeList == NULL) {
    784             result = value == -1 ||    // empty rule
    785                      n == value;       //  'is' rule
    786             break;
    787         }
    788         result = FALSE;                // 'in' or 'within' rule
    789         for (int32_t r=0; r<rangeList->size(); r+=2) {
    790             if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) {
    791                 result = TRUE;
    792                 break;
    793             }
    794         }
    795     } while (FALSE);
    796 
    797     if (negated) {
    798         result = !result;
    799     }
    800     return result;
    801 }
    802 
    803 
    804 AndConstraint*
    805 AndConstraint::add()
    806 {
    807     this->next = new AndConstraint();
    808     return this->next;
    809 }
    810 
    811 OrConstraint::OrConstraint() {
    812     childNode=NULL;
    813     next=NULL;
    814 }
    815 
    816 OrConstraint::OrConstraint(const OrConstraint& other) {
    817     if ( other.childNode == NULL ) {
    818         this->childNode = NULL;
    819     }
    820     else {
    821         this->childNode = new AndConstraint(*(other.childNode));
    822     }
    823     if (other.next == NULL ) {
    824         this->next = NULL;
    825     }
    826     else {
    827         this->next = new OrConstraint(*(other.next));
    828     }
    829 }
    830 
    831 OrConstraint::~OrConstraint() {
    832     if (childNode!=NULL) {
    833         delete childNode;
    834     }
    835     if (next!=NULL) {
    836         delete next;
    837     }
    838 }
    839 
    840 AndConstraint*
    841 OrConstraint::add()
    842 {
    843     OrConstraint *curOrConstraint=this;
    844     {
    845         while (curOrConstraint->next!=NULL) {
    846             curOrConstraint = curOrConstraint->next;
    847         }
    848         U_ASSERT(curOrConstraint->childNode == NULL);
    849         curOrConstraint->childNode = new AndConstraint();
    850     }
    851     return curOrConstraint->childNode;
    852 }
    853 
    854 UBool
    855 OrConstraint::isFulfilled(const FixedDecimal &number) {
    856     OrConstraint* orRule=this;
    857     UBool result=FALSE;
    858 
    859     while (orRule!=NULL && !result) {
    860         result=TRUE;
    861         AndConstraint* andRule = orRule->childNode;
    862         while (andRule!=NULL && result) {
    863             result = andRule->isFulfilled(number);
    864             andRule=andRule->next;
    865         }
    866         orRule = orRule->next;
    867     }
    868 
    869     return result;
    870 }
    871 
    872 
    873 RuleChain::RuleChain(): fKeyword(), fNext(NULL), ruleHeader(NULL), fDecimalSamples(), fIntegerSamples(),
    874                         fDecimalSamplesUnbounded(FALSE), fIntegerSamplesUnbounded(FALSE) {
    875 }
    876 
    877 RuleChain::RuleChain(const RuleChain& other) :
    878         fKeyword(other.fKeyword), fNext(NULL), ruleHeader(NULL), fDecimalSamples(other.fDecimalSamples),
    879         fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded),
    880         fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded) {
    881     if (other.ruleHeader != NULL) {
    882         this->ruleHeader = new OrConstraint(*(other.ruleHeader));
    883     }
    884     if (other.fNext != NULL ) {
    885         this->fNext = new RuleChain(*other.fNext);
    886     }
    887 }
    888 
    889 RuleChain::~RuleChain() {
    890     delete fNext;
    891     delete ruleHeader;
    892 }
    893 
    894 
    895 UnicodeString
    896 RuleChain::select(const FixedDecimal &number) const {
    897     if (!number.isNanOrInfinity) {
    898         for (const RuleChain *rules = this; rules != NULL; rules = rules->fNext) {
    899              if (rules->ruleHeader->isFulfilled(number)) {
    900                  return rules->fKeyword;
    901              }
    902         }
    903     }
    904     return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5);
    905 }
    906 
    907 static UnicodeString tokenString(tokenType tok) {
    908     UnicodeString s;
    909     switch (tok) {
    910       case tVariableN:
    911         s.append(LOW_N); break;
    912       case tVariableI:
    913         s.append(LOW_I); break;
    914       case tVariableF:
    915         s.append(LOW_F); break;
    916       case tVariableV:
    917         s.append(LOW_V); break;
    918       case tVariableT:
    919         s.append(LOW_T); break;
    920       default:
    921         s.append(TILDE);
    922     }
    923     return s;
    924 }
    925 
    926 void
    927 RuleChain::dumpRules(UnicodeString& result) {
    928     UChar digitString[16];
    929 
    930     if ( ruleHeader != NULL ) {
    931         result +=  fKeyword;
    932         result += COLON;
    933         result += SPACE;
    934         OrConstraint* orRule=ruleHeader;
    935         while ( orRule != NULL ) {
    936             AndConstraint* andRule=orRule->childNode;
    937             while ( andRule != NULL ) {
    938                 if ((andRule->op==AndConstraint::NONE) &&  (andRule->rangeList==NULL) && (andRule->value == -1)) {
    939                     // Empty Rules.
    940                 } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) ) {
    941                     result += tokenString(andRule->digitsType);
    942                     result += UNICODE_STRING_SIMPLE(" is ");
    943                     if (andRule->negated) {
    944                         result += UNICODE_STRING_SIMPLE("not ");
    945                     }
    946                     uprv_itou(digitString,16, andRule->value,10,0);
    947                     result += UnicodeString(digitString);
    948                 }
    949                 else {
    950                     result += tokenString(andRule->digitsType);
    951                     result += SPACE;
    952                     if (andRule->op==AndConstraint::MOD) {
    953                         result += UNICODE_STRING_SIMPLE("mod ");
    954                         uprv_itou(digitString,16, andRule->opNum,10,0);
    955                         result += UnicodeString(digitString);
    956                     }
    957                     if (andRule->rangeList==NULL) {
    958                         if (andRule->negated) {
    959                             result += UNICODE_STRING_SIMPLE(" is not ");
    960                             uprv_itou(digitString,16, andRule->value,10,0);
    961                             result += UnicodeString(digitString);
    962                         }
    963                         else {
    964                             result += UNICODE_STRING_SIMPLE(" is ");
    965                             uprv_itou(digitString,16, andRule->value,10,0);
    966                             result += UnicodeString(digitString);
    967                         }
    968                     }
    969                     else {
    970                         if (andRule->negated) {
    971                             if ( andRule->integerOnly ) {
    972                                 result += UNICODE_STRING_SIMPLE(" not in ");
    973                             }
    974                             else {
    975                                 result += UNICODE_STRING_SIMPLE(" not within ");
    976                             }
    977                         }
    978                         else {
    979                             if ( andRule->integerOnly ) {
    980                                 result += UNICODE_STRING_SIMPLE(" in ");
    981                             }
    982                             else {
    983                                 result += UNICODE_STRING_SIMPLE(" within ");
    984                             }
    985                         }
    986                         for (int32_t r=0; r<andRule->rangeList->size(); r+=2) {
    987                             int32_t rangeLo = andRule->rangeList->elementAti(r);
    988                             int32_t rangeHi = andRule->rangeList->elementAti(r+1);
    989                             uprv_itou(digitString,16, rangeLo, 10, 0);
    990                             result += UnicodeString(digitString);
    991                             result += UNICODE_STRING_SIMPLE("..");
    992                             uprv_itou(digitString,16, rangeHi, 10,0);
    993                             result += UnicodeString(digitString);
    994                             if (r+2 < andRule->rangeList->size()) {
    995                                 result += UNICODE_STRING_SIMPLE(", ");
    996                             }
    997                         }
    998                     }
    999                 }
   1000                 if ( (andRule=andRule->next) != NULL) {
   1001                     result += UNICODE_STRING_SIMPLE(" and ");
   1002                 }
   1003             }
   1004             if ( (orRule = orRule->next) != NULL ) {
   1005                 result += UNICODE_STRING_SIMPLE(" or ");
   1006             }
   1007         }
   1008     }
   1009     if ( fNext != NULL ) {
   1010         result += UNICODE_STRING_SIMPLE("; ");
   1011         fNext->dumpRules(result);
   1012     }
   1013 }
   1014 
   1015 
   1016 UErrorCode
   1017 RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const {
   1018     if ( arraySize < capacityOfKeywords-1 ) {
   1019         keywords[arraySize++]=fKeyword;
   1020     }
   1021     else {
   1022         return U_BUFFER_OVERFLOW_ERROR;
   1023     }
   1024 
   1025     if ( fNext != NULL ) {
   1026         return fNext->getKeywords(capacityOfKeywords, keywords, arraySize);
   1027     }
   1028     else {
   1029         return U_ZERO_ERROR;
   1030     }
   1031 }
   1032 
   1033 UBool
   1034 RuleChain::isKeyword(const UnicodeString& keywordParam) const {
   1035     if ( fKeyword == keywordParam ) {
   1036         return TRUE;
   1037     }
   1038 
   1039     if ( fNext != NULL ) {
   1040         return fNext->isKeyword(keywordParam);
   1041     }
   1042     else {
   1043         return FALSE;
   1044     }
   1045 }
   1046 
   1047 
   1048 PluralRuleParser::PluralRuleParser() :
   1049         ruleIndex(0), token(), type(none), prevType(none),
   1050         curAndConstraint(NULL), currentChain(NULL), rangeLowIdx(-1), rangeHiIdx(-1)
   1051 {
   1052 }
   1053 
   1054 PluralRuleParser::~PluralRuleParser() {
   1055 }
   1056 
   1057 
   1058 int32_t
   1059 PluralRuleParser::getNumberValue(const UnicodeString& token) {
   1060     int32_t i;
   1061     char digits[128];
   1062 
   1063     i = token.extract(0, token.length(), digits, UPRV_LENGTHOF(digits), US_INV);
   1064     digits[i]='\0';
   1065 
   1066     return((int32_t)atoi(digits));
   1067 }
   1068 
   1069 
   1070 void
   1071 PluralRuleParser::checkSyntax(UErrorCode &status)
   1072 {
   1073     if (U_FAILURE(status)) {
   1074         return;
   1075     }
   1076     if (!(prevType==none || prevType==tSemiColon)) {
   1077         type = getKeyType(token, type);  // Switch token type from tKeyword if we scanned a reserved word,
   1078                                                //   and we are not at the start of a rule, where a
   1079                                                //   keyword is expected.
   1080     }
   1081 
   1082     switch(prevType) {
   1083     case none:
   1084     case tSemiColon:
   1085         if (type!=tKeyword && type != tEOF) {
   1086             status = U_UNEXPECTED_TOKEN;
   1087         }
   1088         break;
   1089     case tVariableN:
   1090     case tVariableI:
   1091     case tVariableF:
   1092     case tVariableT:
   1093     case tVariableV:
   1094         if (type != tIs && type != tMod && type != tIn &&
   1095             type != tNot && type != tWithin && type != tEqual && type != tNotEqual) {
   1096             status = U_UNEXPECTED_TOKEN;
   1097         }
   1098         break;
   1099     case tKeyword:
   1100         if (type != tColon) {
   1101             status = U_UNEXPECTED_TOKEN;
   1102         }
   1103         break;
   1104     case tColon:
   1105         if (!(type == tVariableN ||
   1106               type == tVariableI ||
   1107               type == tVariableF ||
   1108               type == tVariableT ||
   1109               type == tVariableV ||
   1110               type == tAt)) {
   1111             status = U_UNEXPECTED_TOKEN;
   1112         }
   1113         break;
   1114     case tIs:
   1115         if ( type != tNumber && type != tNot) {
   1116             status = U_UNEXPECTED_TOKEN;
   1117         }
   1118         break;
   1119     case tNot:
   1120         if (type != tNumber && type != tIn && type != tWithin) {
   1121             status = U_UNEXPECTED_TOKEN;
   1122         }
   1123         break;
   1124     case tMod:
   1125     case tDot2:
   1126     case tIn:
   1127     case tWithin:
   1128     case tEqual:
   1129     case tNotEqual:
   1130         if (type != tNumber) {
   1131             status = U_UNEXPECTED_TOKEN;
   1132         }
   1133         break;
   1134     case tAnd:
   1135     case tOr:
   1136         if ( type != tVariableN &&
   1137              type != tVariableI &&
   1138              type != tVariableF &&
   1139              type != tVariableT &&
   1140              type != tVariableV) {
   1141             status = U_UNEXPECTED_TOKEN;
   1142         }
   1143         break;
   1144     case tComma:
   1145         if (type != tNumber) {
   1146             status = U_UNEXPECTED_TOKEN;
   1147         }
   1148         break;
   1149     case tNumber:
   1150         if (type != tDot2  && type != tSemiColon && type != tIs       && type != tNot    &&
   1151             type != tIn    && type != tEqual     && type != tNotEqual && type != tWithin &&
   1152             type != tAnd   && type != tOr        && type != tComma    && type != tAt     &&
   1153             type != tEOF)
   1154         {
   1155             status = U_UNEXPECTED_TOKEN;
   1156         }
   1157         // TODO: a comma following a number that is not part of a range will be allowed.
   1158         //       It's not the only case of this sort of thing. Parser needs a re-write.
   1159         break;
   1160     case tAt:
   1161         if (type != tDecimal && type != tInteger) {
   1162             status = U_UNEXPECTED_TOKEN;
   1163         }
   1164         break;
   1165     default:
   1166         status = U_UNEXPECTED_TOKEN;
   1167         break;
   1168     }
   1169 }
   1170 
   1171 
   1172 /*
   1173  *  Scan the next token from the input rules.
   1174  *     rules and returned token type are in the parser state variables.
   1175  */
   1176 void
   1177 PluralRuleParser::getNextToken(UErrorCode &status)
   1178 {
   1179     if (U_FAILURE(status)) {
   1180         return;
   1181     }
   1182 
   1183     UChar ch;
   1184     while (ruleIndex < ruleSrc->length()) {
   1185         ch = ruleSrc->charAt(ruleIndex);
   1186         type = charType(ch);
   1187         if (type != tSpace) {
   1188             break;
   1189         }
   1190         ++(ruleIndex);
   1191     }
   1192     if (ruleIndex >= ruleSrc->length()) {
   1193         type = tEOF;
   1194         return;
   1195     }
   1196     int32_t curIndex= ruleIndex;
   1197 
   1198     switch (type) {
   1199       case tColon:
   1200       case tSemiColon:
   1201       case tComma:
   1202       case tEllipsis:
   1203       case tTilde:   // scanned '~'
   1204       case tAt:      // scanned '@'
   1205       case tEqual:   // scanned '='
   1206       case tMod:     // scanned '%'
   1207         // Single character tokens.
   1208         ++curIndex;
   1209         break;
   1210 
   1211       case tNotEqual:  // scanned '!'
   1212         if (ruleSrc->charAt(curIndex+1) == EQUALS) {
   1213             curIndex += 2;
   1214         } else {
   1215             type = none;
   1216             curIndex += 1;
   1217         }
   1218         break;
   1219 
   1220       case tKeyword:
   1221          while (type == tKeyword && ++curIndex < ruleSrc->length()) {
   1222              ch = ruleSrc->charAt(curIndex);
   1223              type = charType(ch);
   1224          }
   1225          type = tKeyword;
   1226          break;
   1227 
   1228       case tNumber:
   1229          while (type == tNumber && ++curIndex < ruleSrc->length()) {
   1230              ch = ruleSrc->charAt(curIndex);
   1231              type = charType(ch);
   1232          }
   1233          type = tNumber;
   1234          break;
   1235 
   1236        case tDot:
   1237          // We could be looking at either ".." in a range, or "..." at the end of a sample.
   1238          if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) {
   1239              ++curIndex;
   1240              break; // Single dot
   1241          }
   1242          if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) {
   1243              curIndex += 2;
   1244              type = tDot2;
   1245              break; // double dot
   1246          }
   1247          type = tEllipsis;
   1248          curIndex += 3;
   1249          break;     // triple dot
   1250 
   1251        default:
   1252          status = U_UNEXPECTED_TOKEN;
   1253          ++curIndex;
   1254          break;
   1255     }
   1256 
   1257     U_ASSERT(ruleIndex <= ruleSrc->length());
   1258     U_ASSERT(curIndex <= ruleSrc->length());
   1259     token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex);
   1260     ruleIndex = curIndex;
   1261 }
   1262 
   1263 tokenType
   1264 PluralRuleParser::charType(UChar ch) {
   1265     if ((ch>=U_ZERO) && (ch<=U_NINE)) {
   1266         return tNumber;
   1267     }
   1268     if (ch>=LOW_A && ch<=LOW_Z) {
   1269         return tKeyword;
   1270     }
   1271     switch (ch) {
   1272     case COLON:
   1273         return tColon;
   1274     case SPACE:
   1275         return tSpace;
   1276     case SEMI_COLON:
   1277         return tSemiColon;
   1278     case DOT:
   1279         return tDot;
   1280     case COMMA:
   1281         return tComma;
   1282     case EXCLAMATION:
   1283         return tNotEqual;
   1284     case EQUALS:
   1285         return tEqual;
   1286     case PERCENT_SIGN:
   1287         return tMod;
   1288     case AT:
   1289         return tAt;
   1290     case ELLIPSIS:
   1291         return tEllipsis;
   1292     case TILDE:
   1293         return tTilde;
   1294     default :
   1295         return none;
   1296     }
   1297 }
   1298 
   1299 
   1300 //  Set token type for reserved words in the Plural Rule syntax.
   1301 
   1302 tokenType
   1303 PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType)
   1304 {
   1305     if (keyType != tKeyword) {
   1306         return keyType;
   1307     }
   1308 
   1309     if (0 == token.compare(PK_VAR_N, 1)) {
   1310         keyType = tVariableN;
   1311     } else if (0 == token.compare(PK_VAR_I, 1)) {
   1312         keyType = tVariableI;
   1313     } else if (0 == token.compare(PK_VAR_F, 1)) {
   1314         keyType = tVariableF;
   1315     } else if (0 == token.compare(PK_VAR_T, 1)) {
   1316         keyType = tVariableT;
   1317     } else if (0 == token.compare(PK_VAR_V, 1)) {
   1318         keyType = tVariableV;
   1319     } else if (0 == token.compare(PK_IS, 2)) {
   1320         keyType = tIs;
   1321     } else if (0 == token.compare(PK_AND, 3)) {
   1322         keyType = tAnd;
   1323     } else if (0 == token.compare(PK_IN, 2)) {
   1324         keyType = tIn;
   1325     } else if (0 == token.compare(PK_WITHIN, 6)) {
   1326         keyType = tWithin;
   1327     } else if (0 == token.compare(PK_NOT, 3)) {
   1328         keyType = tNot;
   1329     } else if (0 == token.compare(PK_MOD, 3)) {
   1330         keyType = tMod;
   1331     } else if (0 == token.compare(PK_OR, 2)) {
   1332         keyType = tOr;
   1333     } else if (0 == token.compare(PK_DECIMAL, 7)) {
   1334         keyType = tDecimal;
   1335     } else if (0 == token.compare(PK_INTEGER, 7)) {
   1336         keyType = tInteger;
   1337     }
   1338     return keyType;
   1339 }
   1340 
   1341 
   1342 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status)
   1343         : pos(0), fKeywordNames(status) {
   1344     if (U_FAILURE(status)) {
   1345         return;
   1346     }
   1347     fKeywordNames.setDeleter(uprv_deleteUObject);
   1348     UBool  addKeywordOther=TRUE;
   1349     RuleChain *node=header;
   1350     while(node!=NULL) {
   1351         fKeywordNames.addElement(new UnicodeString(node->fKeyword), status);
   1352         if (U_FAILURE(status)) {
   1353             return;
   1354         }
   1355         if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
   1356             addKeywordOther= FALSE;
   1357         }
   1358         node=node->fNext;
   1359     }
   1360 
   1361     if (addKeywordOther) {
   1362         fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
   1363     }
   1364 }
   1365 
   1366 const UnicodeString*
   1367 PluralKeywordEnumeration::snext(UErrorCode& status) {
   1368     if (U_SUCCESS(status) && pos < fKeywordNames.size()) {
   1369         return (const UnicodeString*)fKeywordNames.elementAt(pos++);
   1370     }
   1371     return NULL;
   1372 }
   1373 
   1374 void
   1375 PluralKeywordEnumeration::reset(UErrorCode& /*status*/) {
   1376     pos=0;
   1377 }
   1378 
   1379 int32_t
   1380 PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
   1381        return fKeywordNames.size();
   1382 }
   1383 
   1384 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
   1385 }
   1386 
   1387 FixedDecimal::FixedDecimal(const VisibleDigits &digits) {
   1388     digits.getFixedDecimal(
   1389             source, intValue, decimalDigits,
   1390             decimalDigitsWithoutTrailingZeros,
   1391             visibleDecimalDigitCount, hasIntegerValue);
   1392     isNegative = digits.isNegative();
   1393     isNanOrInfinity = digits.isNaNOrInfinity();
   1394 }
   1395 
   1396 FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
   1397     init(n, v, f);
   1398     // check values. TODO make into unit test.
   1399     //
   1400     //            long visiblePower = (int) Math.pow(10, v);
   1401     //            if (decimalDigits > visiblePower) {
   1402     //                throw new IllegalArgumentException();
   1403     //            }
   1404     //            double fraction = intValue + (decimalDigits / (double) visiblePower);
   1405     //            if (fraction != source) {
   1406     //                double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source));
   1407     //                if (diff > 0.00000001d) {
   1408     //                    throw new IllegalArgumentException();
   1409     //                }
   1410     //            }
   1411 }
   1412 
   1413 FixedDecimal::FixedDecimal(double n, int32_t v) {
   1414     // Ugly, but for samples we don't care.
   1415     init(n, v, getFractionalDigits(n, v));
   1416 }
   1417 
   1418 FixedDecimal::FixedDecimal(double n) {
   1419     init(n);
   1420 }
   1421 
   1422 FixedDecimal::FixedDecimal() {
   1423     init(0, 0, 0);
   1424 }
   1425 
   1426 
   1427 // Create a FixedDecimal from a UnicodeString containing a number.
   1428 //    Inefficient, but only used for samples, so simplicity trumps efficiency.
   1429 
   1430 FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) {
   1431     CharString cs;
   1432     cs.appendInvariantChars(num, status);
   1433     DigitList dl;
   1434     dl.set(cs.toStringPiece(), status);
   1435     if (U_FAILURE(status)) {
   1436         init(0, 0, 0);
   1437         return;
   1438     }
   1439     int32_t decimalPoint = num.indexOf(DOT);
   1440     double n = dl.getDouble();
   1441     if (decimalPoint == -1) {
   1442         init(n, 0, 0);
   1443     } else {
   1444         int32_t v = num.length() - decimalPoint - 1;
   1445         init(n, v, getFractionalDigits(n, v));
   1446     }
   1447 }
   1448 
   1449 
   1450 FixedDecimal::FixedDecimal(const FixedDecimal &other) {
   1451     source = other.source;
   1452     visibleDecimalDigitCount = other.visibleDecimalDigitCount;
   1453     decimalDigits = other.decimalDigits;
   1454     decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros;
   1455     intValue = other.intValue;
   1456     hasIntegerValue = other.hasIntegerValue;
   1457     isNegative = other.isNegative;
   1458     isNanOrInfinity = other.isNanOrInfinity;
   1459 }
   1460 
   1461 
   1462 void FixedDecimal::init(double n) {
   1463     int32_t numFractionDigits = decimals(n);
   1464     init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
   1465 }
   1466 
   1467 
   1468 void FixedDecimal::init(double n, int32_t v, int64_t f) {
   1469     isNegative = n < 0.0;
   1470     source = fabs(n);
   1471     isNanOrInfinity = uprv_isNaN(source) || uprv_isPositiveInfinity(source);
   1472     if (isNanOrInfinity) {
   1473         v = 0;
   1474         f = 0;
   1475         intValue = 0;
   1476         hasIntegerValue = FALSE;
   1477     } else {
   1478         intValue = (int64_t)source;
   1479         hasIntegerValue = (source == intValue);
   1480     }
   1481 
   1482     visibleDecimalDigitCount = v;
   1483     decimalDigits = f;
   1484     if (f == 0) {
   1485          decimalDigitsWithoutTrailingZeros = 0;
   1486     } else {
   1487         int64_t fdwtz = f;
   1488         while ((fdwtz%10) == 0) {
   1489             fdwtz /= 10;
   1490         }
   1491         decimalDigitsWithoutTrailingZeros = fdwtz;
   1492     }
   1493 }
   1494 
   1495 
   1496 //  Fast path only exact initialization. Return true if successful.
   1497 //     Note: Do not multiply by 10 each time through loop, rounding cruft can build
   1498 //           up that makes the check for an integer result fail.
   1499 //           A single multiply of the original number works more reliably.
   1500 static int32_t p10[] = {1, 10, 100, 1000, 10000};
   1501 UBool FixedDecimal::quickInit(double n) {
   1502     UBool success = FALSE;
   1503     n = fabs(n);
   1504     int32_t numFractionDigits;
   1505     for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) {
   1506         double scaledN = n * p10[numFractionDigits];
   1507         if (scaledN == floor(scaledN)) {
   1508             success = TRUE;
   1509             break;
   1510         }
   1511     }
   1512     if (success) {
   1513         init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
   1514     }
   1515     return success;
   1516 }
   1517 
   1518 
   1519 
   1520 int32_t FixedDecimal::decimals(double n) {
   1521     // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros.
   1522     // fastpath the common cases, integers or fractions with 3 or fewer digits
   1523     n = fabs(n);
   1524     for (int ndigits=0; ndigits<=3; ndigits++) {
   1525         double scaledN = n * p10[ndigits];
   1526         if (scaledN == floor(scaledN)) {
   1527             return ndigits;
   1528         }
   1529     }
   1530 
   1531     // Slow path, convert with sprintf, parse converted output.
   1532     char  buf[30] = {0};
   1533     sprintf(buf, "%1.15e", n);
   1534     // formatted number looks like this: 1.234567890123457e-01
   1535     int exponent = atoi(buf+18);
   1536     int numFractionDigits = 15;
   1537     for (int i=16; ; --i) {
   1538         if (buf[i] != '0') {
   1539             break;
   1540         }
   1541         --numFractionDigits;
   1542     }
   1543     numFractionDigits -= exponent;   // Fraction part of fixed point representation.
   1544     return numFractionDigits;
   1545 }
   1546 
   1547 
   1548 // Get the fraction digits of a double, represented as an integer.
   1549 //    v is the number of visible fraction digits in the displayed form of the number.
   1550 //       Example: n = 1001.234, v = 6, result = 234000
   1551 //    TODO: need to think through how this is used in the plural rule context.
   1552 //          This function can easily encounter integer overflow,
   1553 //          and can easily return noise digits when the precision of a double is exceeded.
   1554 
   1555 int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) {
   1556     if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) {
   1557         return 0;
   1558     }
   1559     n = fabs(n);
   1560     double fract = n - floor(n);
   1561     switch (v) {
   1562       case 1: return (int64_t)(fract*10.0 + 0.5);
   1563       case 2: return (int64_t)(fract*100.0 + 0.5);
   1564       case 3: return (int64_t)(fract*1000.0 + 0.5);
   1565       default:
   1566           double scaled = floor(fract * pow(10.0, (double)v) + 0.5);
   1567           if (scaled > U_INT64_MAX) {
   1568               return U_INT64_MAX;
   1569           } else {
   1570               return (int64_t)scaled;
   1571           }
   1572       }
   1573 }
   1574 
   1575 
   1576 void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) {
   1577     int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount;
   1578     if (numTrailingFractionZeros > 0) {
   1579         for (int32_t i=0; i<numTrailingFractionZeros; i++) {
   1580             // Do not let the decimalDigits value overflow if there are many trailing zeros.
   1581             // Limit the value to 18 digits, the most that a 64 bit int can fully represent.
   1582             if (decimalDigits >= 100000000000000000LL) {
   1583                 break;
   1584             }
   1585             decimalDigits *= 10;
   1586         }
   1587         visibleDecimalDigitCount += numTrailingFractionZeros;
   1588     }
   1589 }
   1590 
   1591 
   1592 double FixedDecimal::get(tokenType operand) const {
   1593     switch(operand) {
   1594         case tVariableN: return source;
   1595         case tVariableI: return (double)intValue;
   1596         case tVariableF: return (double)decimalDigits;
   1597         case tVariableT: return (double)decimalDigitsWithoutTrailingZeros;
   1598         case tVariableV: return visibleDecimalDigitCount;
   1599         default:
   1600              U_ASSERT(FALSE);  // unexpected.
   1601              return source;
   1602     }
   1603 }
   1604 
   1605 int32_t FixedDecimal::getVisibleFractionDigitCount() const {
   1606     return visibleDecimalDigitCount;
   1607 }
   1608 
   1609 
   1610 
   1611 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
   1612     fLocales = NULL;
   1613     fRes = NULL;
   1614     fOpenStatus = status;
   1615     if (U_FAILURE(status)) {
   1616         return;
   1617     }
   1618     fOpenStatus = U_ZERO_ERROR;
   1619     LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &fOpenStatus));
   1620     fLocales = ures_getByKey(rb.getAlias(), "locales", NULL, &fOpenStatus);
   1621 }
   1622 
   1623 PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() {
   1624     ures_close(fLocales);
   1625     ures_close(fRes);
   1626     fLocales = NULL;
   1627     fRes = NULL;
   1628 }
   1629 
   1630 const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) {
   1631     if (U_FAILURE(status)) {
   1632         return NULL;
   1633     }
   1634     if (U_FAILURE(fOpenStatus)) {
   1635         status = fOpenStatus;
   1636         return NULL;
   1637     }
   1638     fRes = ures_getNextResource(fLocales, fRes, &status);
   1639     if (fRes == NULL || U_FAILURE(status)) {
   1640         if (status == U_INDEX_OUTOFBOUNDS_ERROR) {
   1641             status = U_ZERO_ERROR;
   1642         }
   1643         return NULL;
   1644     }
   1645     const char *result = ures_getKey(fRes);
   1646     if (resultLength != NULL) {
   1647         *resultLength = uprv_strlen(result);
   1648     }
   1649     return result;
   1650 }
   1651 
   1652 
   1653 void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) {
   1654     if (U_FAILURE(status)) {
   1655        return;
   1656     }
   1657     if (U_FAILURE(fOpenStatus)) {
   1658         status = fOpenStatus;
   1659         return;
   1660     }
   1661     ures_resetIterator(fLocales);
   1662 }
   1663 
   1664 int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const {
   1665     if (U_FAILURE(status)) {
   1666         return 0;
   1667     }
   1668     if (U_FAILURE(fOpenStatus)) {
   1669         status = fOpenStatus;
   1670         return 0;
   1671     }
   1672     return ures_getSize(fLocales);
   1673 }
   1674 
   1675 U_NAMESPACE_END
   1676 
   1677 
   1678 #endif /* #if !UCONFIG_NO_FORMATTING */
   1679 
   1680 //eof
   1681