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