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