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