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