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