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