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