Coverage Report

Created: 2025-06-24 06:43

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