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