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