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