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