Coverage Report

Created: 2018-09-25 14:53

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