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