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