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