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