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