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