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