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