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