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