Coverage Report

Created: 2026-03-31 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/nfsubs.cpp
Line
Count
Source
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
******************************************************************************
5
*   Copyright (C) 1997-2015, International Business Machines
6
*   Corporation and others.  All Rights Reserved.
7
******************************************************************************
8
*   file name:  nfsubs.cpp
9
*   encoding:   UTF-8
10
*   tab size:   8 (not used)
11
*   indentation:4
12
*
13
* Modification history
14
* Date        Name      Comments
15
* 10/11/2001  Doug      Ported from ICU4J
16
*/
17
18
#include <stdio.h>
19
#include "utypeinfo.h"  // for 'typeid' to work
20
21
#include "nfsubs.h"
22
#include "fmtableimp.h"
23
#include "putilimp.h"
24
#include "number_decimalquantity.h"
25
26
#if U_HAVE_RBNF
27
28
static const char16_t gLessThan = 0x003c;
29
static const char16_t gEquals = 0x003d;
30
static const char16_t gGreaterThan = 0x003e;
31
static const char16_t gPercent = 0x0025;
32
static const char16_t gPound = 0x0023;
33
static const char16_t gZero = 0x0030;
34
static const char16_t gSpace = 0x0020;
35
36
static const char16_t gEqualsEquals[] =
37
{
38
    0x3D, 0x3D, 0
39
}; /* "==" */
40
static const char16_t gGreaterGreaterGreaterThan[] =
41
{
42
    0x3E, 0x3E, 0x3E, 0
43
}; /* ">>>" */
44
static const char16_t gGreaterGreaterThan[] =
45
{
46
    0x3E, 0x3E, 0
47
}; /* ">>" */
48
49
U_NAMESPACE_BEGIN
50
51
using number::impl::DecimalQuantity;
52
53
class SameValueSubstitution : public NFSubstitution {
54
public:
55
    SameValueSubstitution(int32_t pos,
56
        const NFRuleSet* ruleset,
57
        const UnicodeString& description,
58
        UErrorCode& status);
59
    virtual ~SameValueSubstitution();
60
61
0
    virtual int64_t transformNumber(int64_t number) const override { return number; }
62
0
    virtual double transformNumber(double number) const override { return number; }
63
645k
    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return newRuleValue; }
64
1.56M
    virtual double calcUpperBound(double oldUpperBound) const override { return oldUpperBound; }
65
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003d); } // '='
66
67
public:
68
    static UClassID getStaticClassID();
69
    virtual UClassID getDynamicClassID() const override;
70
};
71
72
166k
SameValueSubstitution::~SameValueSubstitution() {}
73
74
class MultiplierSubstitution : public NFSubstitution {
75
    int64_t divisor;
76
    const NFRule* owningRule;
77
78
public:
79
    MultiplierSubstitution(int32_t _pos,
80
        const NFRule *rule,
81
        const NFRuleSet* _ruleSet,
82
        const UnicodeString& description,
83
        UErrorCode& status)
84
233k
        : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor()), owningRule(rule)
85
233k
    {
86
233k
        if (divisor == 0) {
87
1
            status = U_PARSE_ERROR;
88
1
        }
89
233k
    }
90
    virtual ~MultiplierSubstitution();
91
92
39.7k
    virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override {
93
39.7k
        divisor = util64_pow(radix, exponent);
94
95
39.7k
        if(divisor == 0) {
96
0
            status = U_PARSE_ERROR;
97
0
        }
98
39.7k
    }
99
100
    virtual bool operator==(const NFSubstitution& rhs) const override;
101
102
0
    virtual int64_t transformNumber(int64_t number) const override {
103
0
        return number / divisor;
104
0
    }
105
106
0
    virtual double transformNumber(double number) const override {
107
        // Most of the time, when a number is handled by an NFSubstitution, we do a floor() on it, but
108
        // if a substitution uses a DecimalFormat to format the number instead of a ruleset, we generally
109
        // don't want to do a floor()-- we want to keep the value intact so that the DecimalFormat can
110
        // either include the fractional part or round properly.  The big exception to this is here in
111
        // MultiplierSubstitution.  If the rule includes two substitutions, the MultiplierSubstitution
112
        // (which is handling the larger part of the number) really _does_ want to do a floor(), because
113
        // the ModulusSubstitution (which is handling the smaller part of the number) will take
114
        // care of the fractional part.  (Consider something like `1/12: <0< feet >0.0> inches;`.)
115
        // But if there is no ModulusSubstitution, we're shortening the number in some way-- the "larger part"
116
        // of the number is the only part we're keeping.  Even if the DecimalFormat doesn't include the
117
        // fractional part in its output, we still want it to round.  (Consider something like `1/1000: <0<K;`.)
118
        // (TODO: The kRoundFloor thing is a kludge to preserve the previous floor-always behavior.  What we
119
        // probably really want to do is just set the rounding mode on the DecimalFormat to match the rounding
120
        // mode on the RuleBasedNumberFormat and then pass the number to it whole and let it do its own rounding.
121
        // But before making that change, we'd have to make sure it didn't have undesirable side effects.)
122
0
        if (getRuleSet() != nullptr || owningRule->hasModulusSubstitution() || owningRule->formatter->getRoundingMode() == NumberFormat::kRoundFloor) {
123
0
            return uprv_floor(number / divisor);
124
0
        } else {
125
0
            return number / divisor;
126
0
        }
127
0
    }
128
129
102k
    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override {
130
102k
        return newRuleValue * divisor;
131
102k
    }
132
133
1.35M
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast<double>(divisor); }
134
135
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
136
137
public:
138
    static UClassID getStaticClassID();
139
    virtual UClassID getDynamicClassID() const override;
140
};
141
142
232k
MultiplierSubstitution::~MultiplierSubstitution() {}
143
144
class ModulusSubstitution : public NFSubstitution {
145
    int64_t  divisor;
146
    const NFRule* ruleToUse;
147
public:
148
    ModulusSubstitution(int32_t pos,
149
        const NFRule* rule,
150
        const NFRule* rulePredecessor,
151
        const NFRuleSet* ruleSet,
152
        const UnicodeString& description,
153
        UErrorCode& status);
154
    virtual ~ModulusSubstitution();
155
156
95.4k
    virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override {
157
95.4k
        divisor = util64_pow(radix, exponent);
158
159
95.4k
        if (divisor == 0) {
160
0
            status = U_PARSE_ERROR;
161
0
        }
162
95.4k
    }
163
164
    virtual bool operator==(const NFSubstitution& rhs) const override;
165
166
    virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
167
    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
168
169
427
    virtual int64_t transformNumber(int64_t number) const override { return number % divisor; }
170
0
    virtual double transformNumber(double number) const override { return uprv_fmod(number, static_cast<double>(divisor)); }
171
172
    virtual UBool doParse(const UnicodeString& text, 
173
        ParsePosition& parsePosition,
174
        double baseValue,
175
        double upperBound,
176
        UBool lenientParse,
177
        uint32_t nonNumericalExecutedRuleMask,
178
        int32_t recursionCount,
179
        Formattable& result) const override;
180
181
550k
    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override {
182
550k
        return oldRuleValue - uprv_fmod(oldRuleValue, static_cast<double>(divisor)) + newRuleValue;
183
550k
    }
184
185
7.44M
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast<double>(divisor); }
186
187
418
    virtual UBool isModulusSubstitution() const override { return true; }
188
189
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
190
191
    virtual void toString(UnicodeString& result) const override;
192
193
public:
194
    static UClassID getStaticClassID();
195
    virtual UClassID getDynamicClassID() const override;
196
};
197
198
1.00M
ModulusSubstitution::~ModulusSubstitution() {}
199
200
class IntegralPartSubstitution : public NFSubstitution {
201
public:
202
    IntegralPartSubstitution(int32_t _pos,
203
        const NFRuleSet* _ruleSet,
204
        const UnicodeString& description,
205
        UErrorCode& status)
206
14.0k
        : NFSubstitution(_pos, _ruleSet, description, status) {}
207
    virtual ~IntegralPartSubstitution();
208
209
0
    virtual int64_t transformNumber(int64_t number) const override { return number; }
210
0
    virtual double transformNumber(double number) const override { return uprv_floor(number); }
211
607
    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; }
212
9.71k
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; }
213
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
214
215
public:
216
    static UClassID getStaticClassID();
217
    virtual UClassID getDynamicClassID() const override;
218
};
219
220
14.0k
IntegralPartSubstitution::~IntegralPartSubstitution() {}
221
222
class FractionalPartSubstitution : public NFSubstitution {
223
    UBool byDigits;
224
    UBool useSpaces;
225
    enum { kMaxDecimalDigits = 8 };
226
public:
227
    FractionalPartSubstitution(int32_t pos,
228
        const NFRuleSet* ruleSet,
229
        const UnicodeString& description,
230
        UErrorCode& status);
231
    virtual ~FractionalPartSubstitution();
232
233
    virtual bool operator==(const NFSubstitution& rhs) const override;
234
235
    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
236
0
    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {}
237
0
    virtual int64_t transformNumber(int64_t /*number*/) const override { return 0; }
238
0
    virtual double transformNumber(double number) const override { return number - uprv_floor(number); }
239
240
    virtual UBool doParse(const UnicodeString& text,
241
        ParsePosition& parsePosition,
242
        double baseValue,
243
        double upperBound,
244
        UBool lenientParse,
245
        uint32_t nonNumericalExecutedRuleMask,
246
        int32_t recursionCount,
247
        Formattable& result) const override;
248
249
37.7k
    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; }
250
1.46k
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return 0.0; }
251
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
252
253
public:
254
    static UClassID getStaticClassID();
255
    virtual UClassID getDynamicClassID() const override;
256
};
257
258
23.3k
FractionalPartSubstitution::~FractionalPartSubstitution() {}
259
260
class AbsoluteValueSubstitution : public NFSubstitution {
261
public:
262
    AbsoluteValueSubstitution(int32_t _pos,
263
        const NFRuleSet* _ruleSet,
264
        const UnicodeString& description,
265
        UErrorCode& status)
266
61.3k
        : NFSubstitution(_pos, _ruleSet, description, status) {}
267
    virtual ~AbsoluteValueSubstitution();
268
269
0
    virtual int64_t transformNumber(int64_t number) const override { return number >= 0 ? number : -number; }
270
0
    virtual double transformNumber(double number) const override { return uprv_fabs(number); }
271
283
    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return -newRuleValue; }
272
12.1k
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; }
273
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
274
275
public:
276
    static UClassID getStaticClassID();
277
    virtual UClassID getDynamicClassID() const override;
278
};
279
280
61.3k
AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
281
282
class NumeratorSubstitution : public NFSubstitution {
283
    double denominator;
284
    int64_t ldenominator;
285
    UBool withZeros;
286
public:
287
4.16k
    static inline UnicodeString fixdesc(const UnicodeString& desc) {
288
4.16k
        if (desc.endsWith(LTLT, 2)) {
289
1.58k
            UnicodeString result(desc, 0, desc.length()-1);
290
1.58k
            return result;
291
1.58k
        }
292
2.58k
        return desc;
293
4.16k
    }
294
    NumeratorSubstitution(int32_t _pos,
295
        double _denominator,
296
        NFRuleSet* _ruleSet,
297
        const UnicodeString& description,
298
        UErrorCode& status)
299
4.16k
        : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator) 
300
4.16k
    {
301
4.16k
        ldenominator = util64_fromDouble(denominator);
302
4.16k
        withZeros = description.endsWith(LTLT, 2);
303
4.16k
    }
304
    virtual ~NumeratorSubstitution();
305
306
    virtual bool operator==(const NFSubstitution& rhs) const override;
307
308
0
    virtual int64_t transformNumber(int64_t number) const override { return number * ldenominator; }
309
0
    virtual double transformNumber(double number) const override { return uprv_round(number * denominator); }
310
311
0
    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {}
312
    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
313
    virtual UBool doParse(const UnicodeString& text, 
314
        ParsePosition& parsePosition,
315
        double baseValue,
316
        double upperBound,
317
        UBool /*lenientParse*/,
318
        uint32_t nonNumericalExecutedRuleMask,
319
        int32_t recursionCount,
320
        Formattable& result) const override;
321
322
28.5k
    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue / oldRuleValue; }
323
275k
    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return denominator; }
324
0
    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
325
private:
326
    static const char16_t LTLT[2];
327
328
public:
329
    static UClassID getStaticClassID();
330
    virtual UClassID getDynamicClassID() const override;
331
};
332
333
4.16k
NumeratorSubstitution::~NumeratorSubstitution() {}
334
335
NFSubstitution*
336
NFSubstitution::makeSubstitution(int32_t pos,
337
                                 const NFRule* rule,
338
                                 const NFRule* predecessor,
339
                                 const NFRuleSet* ruleSet,
340
                                 const RuleBasedNumberFormat* formatter,
341
                                 const UnicodeString& description,
342
                                 UErrorCode& status)
343
1.50M
{
344
1.50M
    if (U_FAILURE(status)) return nullptr;
345
    // if the description is empty, return a NullSubstitution
346
1.50M
    if (description.length() == 0) {
347
0
        return nullptr;
348
0
    }
349
350
1.50M
    switch (description.charAt(0)) {
351
        // if the description begins with '<'...
352
251k
    case gLessThan:
353
        // throw an exception if the rule is a negative number
354
        // rule
355
251k
        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
356
            // throw new IllegalArgumentException("<< not allowed in negative-number rule");
357
2
            status = U_PARSE_ERROR;
358
2
            return nullptr;
359
2
        }
360
361
        // if the rule is a fraction rule, return an
362
        // IntegralPartSubstitution
363
251k
        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
364
239k
            || rule->getBaseValue() == NFRule::kProperFractionRule
365
238k
            || rule->getBaseValue() == NFRule::kDefaultRule) {
366
14.0k
            return new IntegralPartSubstitution(pos, ruleSet, description, status);
367
14.0k
        }
368
369
        // if the rule set containing the rule is a fraction
370
        // rule set, return a NumeratorSubstitution
371
237k
        else if (ruleSet->isFractionRuleSet()) {
372
4.16k
            return new NumeratorSubstitution(pos, static_cast<double>(rule->getBaseValue()),
373
4.16k
                formatter->getDefaultRuleSet(), description, status);
374
4.16k
        }
375
376
        // otherwise, return a MultiplierSubstitution
377
233k
        else {
378
233k
            return new MultiplierSubstitution(pos, rule, ruleSet,
379
233k
                description, status);
380
233k
        }
381
382
        // if the description begins with '>'...
383
1.08M
    case gGreaterThan:
384
        // if the rule is a negative-number rule, return
385
        // an AbsoluteValueSubstitution
386
1.08M
        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
387
61.3k
            return new AbsoluteValueSubstitution(pos, ruleSet, description, status);
388
61.3k
        }
389
390
        // if the rule is a fraction rule, return a
391
        // FractionalPartSubstitution
392
1.02M
        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
393
1.01M
            || rule->getBaseValue() == NFRule::kProperFractionRule
394
1.01M
            || rule->getBaseValue() == NFRule::kDefaultRule) {
395
23.3k
            return new FractionalPartSubstitution(pos, ruleSet, description, status);
396
23.3k
        }
397
398
        // if the rule set owning the rule is a fraction rule set,
399
        // throw an exception
400
1.00M
        else if (ruleSet->isFractionRuleSet()) {
401
            // throw new IllegalArgumentException(">> not allowed in fraction rule set");
402
6
            status = U_PARSE_ERROR;
403
6
            return nullptr;
404
6
        }
405
406
        // otherwise, return a ModulusSubstitution
407
1.00M
        else {
408
1.00M
            return new ModulusSubstitution(pos, rule, predecessor,
409
1.00M
                ruleSet, description, status);
410
1.00M
        }
411
412
        // if the description begins with '=', always return a
413
        // SameValueSubstitution
414
166k
    case gEquals:
415
166k
        return new SameValueSubstitution(pos, ruleSet, description, status);
416
417
        // and if it's anything else, throw an exception
418
0
    default:
419
        // throw new IllegalArgumentException("Illegal substitution character");
420
0
        status = U_PARSE_ERROR;
421
1.50M
    }
422
0
    return nullptr;
423
1.50M
}
424
425
NFSubstitution::NFSubstitution(int32_t _pos,
426
                               const NFRuleSet* _ruleSet,
427
                               const UnicodeString& description,
428
                               UErrorCode& status)
429
1.50M
                               : pos(_pos), ruleSet(nullptr), numberFormat(nullptr)
430
1.50M
{
431
1.50M
    if (U_FAILURE(status)) return;
432
    // the description should begin and end with the same character.
433
    // If it doesn't that's a syntax error.  Otherwise,
434
    // makeSubstitution() was the only thing that needed to know
435
    // about these characters, so strip them off
436
1.50M
    UnicodeString workingDescription(description);
437
1.50M
    if (description.length() >= 2
438
1.50M
        && description.charAt(0) == description.charAt(description.length() - 1))
439
1.50M
    {
440
1.50M
        workingDescription.remove(description.length() - 1, 1);
441
1.50M
        workingDescription.remove(0, 1);
442
1.50M
    }
443
122
    else if (description.length() != 0) {
444
        // throw new IllegalArgumentException("Illegal substitution syntax");
445
122
        status = U_PARSE_ERROR;
446
122
        return;
447
122
    }
448
449
1.50M
    if (workingDescription.length() == 0) {
450
        // if the description was just two paired token characters
451
        // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
452
        // format its result
453
1.00M
        this->ruleSet = _ruleSet;
454
1.00M
    }
455
498k
    else if (workingDescription.charAt(0) == gPercent) {
456
        // if the description contains a rule set name, that's the rule
457
        // set we use to format the result: get a reference to the
458
        // names rule set
459
238k
        this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status);
460
238k
    }
461
259k
    else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
462
        // if the description begins with 0 or #, treat it as a
463
        // DecimalFormat pattern, and initialize a DecimalFormat with
464
        // that pattern (then set it to use the DecimalFormatSymbols
465
        // belonging to our formatter)
466
204k
        const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols();
467
204k
        if (!sym) {
468
0
            status = U_MISSING_RESOURCE_ERROR;
469
0
            return;
470
0
        }
471
204k
        DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status);
472
        /* test for nullptr */
473
204k
        if (!tempNumberFormat) {
474
0
            status = U_MEMORY_ALLOCATION_ERROR;
475
0
            return;
476
0
        }
477
204k
        if (U_FAILURE(status)) {
478
5.54k
            delete tempNumberFormat;
479
5.54k
            return;
480
5.54k
        }
481
199k
        this->numberFormat = tempNumberFormat;
482
199k
    }
483
54.9k
    else if (workingDescription.charAt(0) == gGreaterThan) {
484
        // if the description is ">>>", this substitution bypasses the
485
        // usual rule-search process and always uses the rule that precedes
486
        // it in its own rule set's rule list (this is used for place-value
487
        // notations: formats where you want to see a particular part of
488
        // a number even when it's 0)
489
490
        // this causes problems when >>> is used in a frationalPartSubstitution
491
        // this->ruleSet = nullptr;
492
52.3k
        this->ruleSet = _ruleSet;
493
52.3k
        this->numberFormat = nullptr;
494
52.3k
    }
495
2.57k
    else {
496
        // and of the description is none of these things, it's a syntax error
497
498
        // throw new IllegalArgumentException("Illegal substitution syntax");
499
2.57k
        status = U_PARSE_ERROR;
500
2.57k
    }
501
1.50M
}
502
503
NFSubstitution::~NFSubstitution()
504
1.50M
{
505
1.50M
    delete numberFormat;
506
1.50M
    numberFormat = nullptr;
507
1.50M
}
508
509
/**
510
 * Set's the substitution's divisor.  Used by NFRule.setBaseValue().
511
 * A no-op for all substitutions except multiplier and modulus
512
 * substitutions.
513
 * @param radix The radix of the divisor
514
 * @param exponent The exponent of the divisor
515
 */
516
void
517
1.15k
NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) {
518
  // a no-op for all substitutions except multiplier and modulus substitutions
519
1.15k
}
520
521
void
522
0
NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) {
523
0
    if (numberFormat != nullptr) {
524
0
        numberFormat->setDecimalFormatSymbols(newSymbols);
525
0
    }
526
0
}
527
528
//-----------------------------------------------------------------------
529
// boilerplate
530
//-----------------------------------------------------------------------
531
532
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution)
533
534
/**
535
 * Compares two substitutions for equality
536
 * @param The substitution to compare this one to
537
 * @return true if the two substitutions are functionally equivalent
538
 */
539
bool
540
NFSubstitution::operator==(const NFSubstitution& rhs) const
541
0
{
542
  // compare class and all of the fields all substitutions have
543
  // in common
544
  // this should be called by subclasses before their own equality tests
545
0
  return typeid(*this) == typeid(rhs)
546
0
  && pos == rhs.pos
547
0
  && (ruleSet == nullptr) == (rhs.ruleSet == nullptr)
548
  // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
549
0
  && (numberFormat == nullptr
550
0
      ? (rhs.numberFormat == nullptr)
551
0
      : (*numberFormat == *rhs.numberFormat));
552
0
}
553
554
/**
555
 * Returns a textual description of the substitution
556
 * @return A textual description of the substitution.  This might
557
 * not be identical to the description it was created from, but
558
 * it'll produce the same result.
559
 */
560
void
561
NFSubstitution::toString(UnicodeString& text) const
562
0
{
563
  // use tokenChar() to get the character at the beginning and
564
  // end of the substitutin token.  In between them will go
565
  // either the name of the rule set it uses, or the pattern of
566
  // the DecimalFormat it uses
567
0
  text.remove();
568
0
  text.append(tokenChar());
569
570
0
  UnicodeString temp;
571
0
  if (ruleSet != nullptr) {
572
0
    ruleSet->getName(temp);
573
0
  } else if (numberFormat != nullptr) {
574
0
    numberFormat->toPattern(temp);
575
0
  }
576
0
  text.append(temp);
577
0
  text.append(tokenChar());
578
0
}
579
580
//-----------------------------------------------------------------------
581
// formatting
582
//-----------------------------------------------------------------------
583
584
/**
585
 * Performs a mathematical operation on the number, formats it using
586
 * either ruleSet or decimalFormat, and inserts the result into
587
 * toInsertInto.
588
 * @param number The number being formatted.
589
 * @param toInsertInto The string we insert the result into
590
 * @param pos The position in toInsertInto where the owning rule's
591
 * rule text begins (this value is added to this substitution's
592
 * position to determine exactly where to insert the new text)
593
 */
594
void
595
NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
596
427
{
597
427
    if (U_FAILURE(status)) return;
598
427
    if (ruleSet != nullptr) {
599
        // Perform a transformation on the number that is dependent
600
        // on the type of substitution this is, then just call its
601
        // rule set's format() method to format the result
602
427
        ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status);
603
427
    } else if (numberFormat != nullptr) {
604
0
        if (number <= MAX_INT64_IN_DOUBLE) {
605
            // or perform the transformation on the number,
606
            // then use that formatter's format() method
607
            // to format the result
608
0
            UnicodeString temp;
609
0
            numberFormat->format(transformNumber(static_cast<double>(number)), temp, status);
610
0
            toInsertInto.insert(_pos + this->pos, temp);
611
0
        } 
612
0
        else { 
613
            // We have gone beyond double precision. Something has to give. 
614
            // We're favoring accuracy of the large number over potential rules 
615
            // that round like a CompactDecimalFormat, which is not a common use case. 
616
            // 
617
            // Perform a transformation on the number that is dependent 
618
            // on the type of substitution this is, then just call its 
619
            // rule set's format() method to format the result 
620
0
            int64_t numberToFormat = transformNumber(number); 
621
0
            UnicodeString temp;
622
0
            numberFormat->format(numberToFormat, temp, status);
623
0
            toInsertInto.insert(_pos + this->pos, temp);
624
0
        } 
625
0
    }
626
427
}
627
628
/**
629
 * Performs a mathematical operation on the number, formats it using
630
 * either ruleSet or decimalFormat, and inserts the result into
631
 * toInsertInto.
632
 * @param number The number being formatted.
633
 * @param toInsertInto The string we insert the result into
634
 * @param pos The position in toInsertInto where the owning rule's
635
 * rule text begins (this value is added to this substitution's
636
 * position to determine exactly where to insert the new text)
637
 */
638
void
639
0
NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const {
640
0
    if (U_FAILURE(status)) return;
641
    // perform a transformation on the number being formatted that
642
    // is dependent on the type of substitution this is
643
0
    double numberToFormat = transformNumber(number);
644
645
0
    if (uprv_isInfinite(numberToFormat)) {
646
        // This is probably a minus rule. Combine it with an infinite rule.
647
0
        const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity());
648
0
        infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
649
0
        return;
650
0
    }
651
652
    // if the result is an integer, from here on out we work in integer
653
    // space (saving time and memory and preserving accuracy)
654
0
    if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != nullptr) {
655
0
        ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status);
656
657
        // if the result isn't an integer, then call either our rule set's
658
        // format() method or our DecimalFormat's format() method to
659
        // format the result
660
0
    } else {
661
0
        if (ruleSet != nullptr) {
662
0
            ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
663
0
        } else if (numberFormat != nullptr) {
664
0
            UnicodeString temp;
665
0
            numberFormat->format(numberToFormat, temp);
666
0
            toInsertInto.insert(_pos + this->pos, temp);
667
0
        }
668
0
    }
669
0
}
670
671
672
    //-----------------------------------------------------------------------
673
    // parsing
674
    //-----------------------------------------------------------------------
675
676
#ifdef RBNF_DEBUG
677
#include <stdio.h>
678
#endif
679
680
/**
681
 * Parses a string using the rule set or DecimalFormat belonging
682
 * to this substitution.  If there's a match, a mathematical
683
 * operation (the inverse of the one used in formatting) is
684
 * performed on the result of the parse and the value passed in
685
 * and returned as the result.  The parse position is updated to
686
 * point to the first unmatched character in the string.
687
 * @param text The string to parse
688
 * @param parsePosition On entry, ignored, but assumed to be 0.
689
 * On exit, this is updated to point to the first unmatched
690
 * character (or 0 if the substitution didn't match)
691
 * @param baseValue A partial parse result that should be
692
 * combined with the result of this parse
693
 * @param upperBound When searching the rule set for a rule
694
 * matching the string passed in, only rules with base values
695
 * lower than this are considered
696
 * @param lenientParse If true and matching against rules fails,
697
 * the substitution will also try matching the text against
698
 * numerals using a default-costructed NumberFormat.  If false,
699
 * no extra work is done.  (This value is false whenever the
700
 * formatter isn't in lenient-parse mode, but is also false
701
 * under some conditions even when the formatter _is_ in
702
 * lenient-parse mode.)
703
 * @return If there's a match, this is the result of composing
704
 * baseValue with whatever was returned from matching the
705
 * characters.  This will be either a Long or a Double.  If there's
706
 * no match this is new Long(0) (not null), and parsePosition
707
 * is left unchanged.
708
 */
709
UBool
710
NFSubstitution::doParse(const UnicodeString& text,
711
                        ParsePosition& parsePosition,
712
                        double baseValue,
713
                        double upperBound,
714
                        UBool lenientParse,
715
                        uint32_t nonNumericalExecutedRuleMask,
716
                        int32_t recursionCount,
717
                        Formattable& result) const
718
10.6M
{
719
#ifdef RBNF_DEBUG
720
    fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
721
#endif
722
    // figure out the highest base value a rule can have and match
723
    // the text being parsed (this varies according to the type of
724
    // substitutions: multiplier, modulus, and numerator substitutions
725
    // restrict the search to rules with base values lower than their
726
    // own; same-value substitutions leave the upper bound wherever
727
    // it was, and the others allow any rule to match
728
10.6M
    upperBound = calcUpperBound(upperBound);
729
730
    // use our rule set to parse the text.  If that fails and
731
    // lenient parsing is enabled (this is always false if the
732
    // formatter's lenient-parsing mode is off, but it may also
733
    // be false even when the formatter's lenient-parse mode is
734
    // on), then also try parsing the text using a default-
735
    // constructed NumberFormat
736
10.6M
    if (ruleSet != nullptr) {
737
8.38M
        ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
738
8.38M
        if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
739
0
            UErrorCode status = U_ZERO_ERROR;
740
0
            NumberFormat* fmt = NumberFormat::createInstance(status);
741
0
            if (U_SUCCESS(status)) {
742
0
                fmt->parse(text, result, parsePosition);
743
0
            }
744
0
            delete fmt;
745
0
        }
746
747
        // ...or use our DecimalFormat to parse the text
748
8.38M
    } else if (numberFormat != nullptr) {
749
2.26M
        numberFormat->parse(text, result, parsePosition);
750
2.26M
    }
751
752
    // if the parse was successful, we've already advanced the caller's
753
    // parse position (this is the one function that doesn't have one
754
    // of its own).  Derive a parse result and return it as a Long,
755
    // if possible, or a Double
756
10.6M
    if (parsePosition.getIndex() != 0) {
757
1.21M
        UErrorCode status = U_ZERO_ERROR;
758
1.21M
        double tempResult = result.getDouble(status);
759
760
        // composeRuleValue() produces a full parse result from
761
        // the partial parse result passed to this function from
762
        // the caller (this is either the owning rule's base value
763
        // or the partial result obtained from composing the
764
        // owning rule's base value with its other substitution's
765
        // parse result) and the partial parse result obtained by
766
        // matching the substitution (which will be the same value
767
        // the caller would get by parsing just this part of the
768
        // text with RuleBasedNumberFormat.parse() ).  How the two
769
        // values are used to derive the full parse result depends
770
        // on the types of substitutions: For a regular rule, the
771
        // ultimate result is its multiplier substitution's result
772
        // times the rule's divisor (or the rule's base value) plus
773
        // the modulus substitution's result (which will actually
774
        // supersede part of the rule's base value).  For a negative-
775
        // number rule, the result is the negative of its substitution's
776
        // result.  For a fraction rule, it's the sum of its two
777
        // substitution results.  For a rule in a fraction rule set,
778
        // it's the numerator substitution's result divided by
779
        // the rule's base value.  Results from same-value substitutions
780
        // propagate back upard, and null substitutions don't affect
781
        // the result.
782
1.21M
        tempResult = composeRuleValue(tempResult, baseValue);
783
1.21M
        result.setDouble(tempResult);
784
1.21M
        return true;
785
        // if the parse was UNsuccessful, return 0
786
9.43M
    } else {
787
9.43M
        result.setLong(0);
788
9.43M
        return false;
789
9.43M
    }
790
10.6M
}
791
792
    /**
793
     * Returns true if this is a modulus substitution.  (We didn't do this
794
     * with instanceof partially because it causes source files to
795
     * proliferate and partially because we have to port this to C++.)
796
     * @return true if this object is an instance of ModulusSubstitution
797
     */
798
UBool
799
0
NFSubstitution::isModulusSubstitution() const {
800
0
    return false;
801
0
}
802
803
//===================================================================
804
// SameValueSubstitution
805
//===================================================================
806
807
/**
808
 * A substitution that passes the value passed to it through unchanged.
809
 * Represented by == in rule descriptions.
810
 */
811
SameValueSubstitution::SameValueSubstitution(int32_t _pos,
812
                        const NFRuleSet* _ruleSet,
813
                        const UnicodeString& description,
814
                        UErrorCode& status)
815
166k
: NFSubstitution(_pos, _ruleSet, description, status)
816
166k
{
817
166k
    if (0 == description.compare(gEqualsEquals, 2)) {
818
        // throw new IllegalArgumentException("== is not a legal token");
819
396
        status = U_PARSE_ERROR;
820
396
    }
821
166k
}
822
823
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution)
824
825
//===================================================================
826
// MultiplierSubstitution
827
//===================================================================
828
829
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution)
830
831
bool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
832
0
{
833
0
    return NFSubstitution::operator==(rhs) &&
834
0
        divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
835
0
}
836
837
838
//===================================================================
839
// ModulusSubstitution
840
//===================================================================
841
842
/**
843
 * A substitution that divides the number being formatted by the its rule's
844
 * divisor and formats the remainder.  Represented by "&gt;&gt;" in a
845
 * regular rule.
846
 */
847
ModulusSubstitution::ModulusSubstitution(int32_t _pos,
848
                                         const NFRule* rule,
849
                                         const NFRule* predecessor,
850
                                         const NFRuleSet* _ruleSet,
851
                                         const UnicodeString& description,
852
                                         UErrorCode& status)
853
1.00M
 : NFSubstitution(_pos, _ruleSet, description, status)
854
1.00M
 , divisor(rule->getDivisor())
855
1.00M
 , ruleToUse(nullptr)
856
1.00M
{
857
1.00M
  if (U_FAILURE(status)) return;
858
  // the owning rule's divisor controls the behavior of this
859
  // substitution: rather than keeping a backpointer to the rule,
860
  // we keep a copy of the divisor
861
862
999k
  if (divisor == 0) {
863
1
      status = U_PARSE_ERROR;
864
1
  }
865
866
999k
  if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
867
    // the >>> token doesn't alter how this substitution calculates the
868
    // values it uses for formatting and parsing, but it changes
869
    // what's done with that value after it's obtained: >>> short-
870
    // circuits the rule-search process and goes straight to the
871
    // specified rule to format the substitution value
872
39.9k
    ruleToUse = predecessor;
873
39.9k
  }
874
999k
}
875
876
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution)
877
878
bool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
879
0
{
880
0
  return NFSubstitution::operator==(rhs) &&
881
0
  divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
882
0
  ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
883
0
}
884
885
//-----------------------------------------------------------------------
886
// formatting
887
//-----------------------------------------------------------------------
888
889
890
/**
891
 * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
892
 * the substitution.  Otherwise, just use the superclass function.
893
 * @param number The number being formatted
894
 * @toInsertInto The string to insert the result of this substitution
895
 * into
896
 * @param pos The position of the rule text in toInsertInto
897
 */
898
void
899
ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
900
427
{
901
427
    if (U_FAILURE(status)) return;
902
    // if this isn't a >>> substitution, just use the inherited version
903
    // of this function (which uses either a rule set or a DecimalFormat
904
    // to format its substitution value)
905
427
    if (ruleToUse == nullptr) {
906
427
        NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
907
908
        // a >>> substitution goes straight to a particular rule to
909
        // format the substitution value
910
427
    } else {
911
0
        int64_t numberToFormat = transformNumber(number);
912
0
        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
913
0
    }
914
427
}
915
916
/**
917
* If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
918
* the substitution.  Otherwise, just use the superclass function.
919
* @param number The number being formatted
920
* @toInsertInto The string to insert the result of this substitution
921
* into
922
* @param pos The position of the rule text in toInsertInto
923
*/
924
void
925
ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
926
0
{
927
0
    if (U_FAILURE(status)) return;
928
    // if this isn't a >>> substitution, just use the inherited version
929
    // of this function (which uses either a rule set or a DecimalFormat
930
    // to format its substitution value)
931
0
    if (ruleToUse == nullptr) {
932
0
        NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
933
934
        // a >>> substitution goes straight to a particular rule to
935
        // format the substitution value
936
0
    } else {
937
0
        double numberToFormat = transformNumber(number);
938
939
0
        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
940
0
    }
941
0
}
942
943
//-----------------------------------------------------------------------
944
// parsing
945
//-----------------------------------------------------------------------
946
947
/**
948
 * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.
949
 * Otherwise, use the superclass function.
950
 * @param text The string to parse
951
 * @param parsePosition Ignored on entry, updated on exit to point to
952
 * the first unmatched character.
953
 * @param baseValue The partial parse result prior to calling this
954
 * routine.
955
 */
956
UBool
957
ModulusSubstitution::doParse(const UnicodeString& text,
958
                             ParsePosition& parsePosition,
959
                             double baseValue,
960
                             double upperBound,
961
                             UBool lenientParse,
962
                             uint32_t nonNumericalExecutedRuleMask,
963
                             int32_t recursionCount,
964
                             Formattable& result) const
965
12.5M
{
966
    // if this isn't a >>> substitution, we can just use the
967
    // inherited parse() routine to do the parsing
968
12.5M
    if (ruleToUse == nullptr) {
969
7.44M
        return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, recursionCount, result);
970
971
        // but if it IS a >>> substitution, we have to do it here: we
972
        // use the specific rule's doParse() method, and then we have to
973
        // do some of the other work of NFRuleSet.parse()
974
7.44M
    } else {
975
5.14M
        ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
976
977
5.14M
        if (parsePosition.getIndex() != 0) {
978
110k
            UErrorCode status = U_ZERO_ERROR;
979
110k
            double tempResult = result.getDouble(status);
980
110k
            tempResult = composeRuleValue(tempResult, baseValue);
981
110k
            result.setDouble(tempResult);
982
110k
        }
983
984
5.14M
        return true;
985
5.14M
    }
986
12.5M
}
987
/**
988
 * Returns a textual description of the substitution
989
 * @return A textual description of the substitution.  This might
990
 * not be identical to the description it was created from, but
991
 * it'll produce the same result.
992
 */
993
void
994
ModulusSubstitution::toString(UnicodeString& text) const
995
0
{
996
  // use tokenChar() to get the character at the beginning and
997
  // end of the substitutin token.  In between them will go
998
  // either the name of the rule set it uses, or the pattern of
999
  // the DecimalFormat it uses
1000
1001
0
  if ( ruleToUse != nullptr ) { // Must have been a >>> substitution.
1002
0
      text.remove();
1003
0
      text.append(tokenChar());
1004
0
      text.append(tokenChar());
1005
0
      text.append(tokenChar());
1006
0
  } else { // Otherwise just use the super-class function.
1007
0
    NFSubstitution::toString(text);
1008
0
  }
1009
0
}
1010
//===================================================================
1011
// IntegralPartSubstitution
1012
//===================================================================
1013
1014
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution)
1015
1016
1017
//===================================================================
1018
// FractionalPartSubstitution
1019
//===================================================================
1020
1021
1022
    /**
1023
     * Constructs a FractionalPartSubstitution.  This object keeps a flag
1024
     * telling whether it should format by digits or not.  In addition,
1025
     * it marks the rule set it calls (if any) as a fraction rule set.
1026
     */
1027
FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
1028
                             const NFRuleSet* _ruleSet,
1029
                             const UnicodeString& description,
1030
                             UErrorCode& status)
1031
23.3k
 : NFSubstitution(_pos, _ruleSet, description, status)
1032
23.3k
 , byDigits(false)
1033
23.3k
 , useSpaces(true)
1034
1035
23.3k
{
1036
23.3k
    if (U_FAILURE(status)) return;
1037
    // akk, ruleSet can change in superclass constructor
1038
21.9k
    if (0 == description.compare(gGreaterGreaterThan, 2) ||
1039
13.9k
        0 == description.compare(gGreaterGreaterGreaterThan, 3) ||
1040
19.3k
        _ruleSet == getRuleSet()) {
1041
19.3k
        byDigits = true;
1042
19.3k
        if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
1043
11.1k
            useSpaces = false;
1044
11.1k
        }
1045
19.3k
    } else {
1046
        // cast away const
1047
2.66k
        NFRuleSet* rs = const_cast<NFRuleSet*>(getRuleSet());
1048
2.66k
        if (rs != nullptr) {
1049
1.84k
            rs->makeIntoFractionRuleSet();
1050
1.84k
        } else {
1051
819
            status = U_PARSE_ERROR;
1052
819
        }
1053
2.66k
    }
1054
21.9k
}
1055
1056
//-----------------------------------------------------------------------
1057
// formatting
1058
//-----------------------------------------------------------------------
1059
1060
/**
1061
 * If in "by digits" mode, fills in the substitution one decimal digit
1062
 * at a time using the rule set containing this substitution.
1063
 * Otherwise, uses the superclass function.
1064
 * @param number The number being formatted
1065
 * @param toInsertInto The string to insert the result of formatting
1066
 * the substitution into
1067
 * @param pos The position of the owning rule's rule text in
1068
 * toInsertInto
1069
 */
1070
void
1071
FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto,
1072
                                           int32_t _pos, int32_t recursionCount, UErrorCode& status) const
1073
0
{
1074
0
  if (U_FAILURE(status)) return;
1075
  // if we're not in "byDigits" mode, just use the inherited
1076
  // doSubstitution() routine
1077
0
  if (!byDigits) {
1078
0
    NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
1079
1080
    // if we're in "byDigits" mode, transform the value into an integer
1081
    // by moving the decimal point eight places to the right and
1082
    // pulling digits off the right one at a time, formatting each digit
1083
    // as an integer using this substitution's owning rule set
1084
    // (this is slower, but more accurate, than doing it from the
1085
    // other end)
1086
0
  } else {
1087
    //          int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
1088
    //          // this flag keeps us from formatting trailing zeros.  It starts
1089
    //          // out false because we're pulling from the right, and switches
1090
    //          // to true the first time we encounter a non-zero digit
1091
    //          UBool doZeros = false;
1092
    //          for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
1093
    //              int64_t digit = numberToFormat % 10;
1094
    //              if (digit != 0 || doZeros) {
1095
    //                  if (doZeros && useSpaces) {
1096
    //                      toInsertInto.insert(_pos + getPos(), gSpace);
1097
    //                  }
1098
    //                  doZeros = true;
1099
    //                  getRuleSet()->format(digit, toInsertInto, _pos + getPos());
1100
    //              }
1101
    //              numberToFormat /= 10;
1102
    //          }
1103
1104
0
    DecimalQuantity dl;
1105
0
    dl.setToDouble(number);
1106
0
    dl.roundToMagnitude(-20, UNUM_ROUND_HALFEVEN, status);     // round to 20 fraction digits.
1107
    
1108
0
    UBool pad = false;
1109
0
    for (int32_t didx = dl.getLowerDisplayMagnitude(); didx<0; didx++) {
1110
      // Loop iterates over fraction digits, starting with the LSD.
1111
      //   include both real digits from the number, and zeros
1112
      //   to the left of the MSD but to the right of the decimal point.
1113
0
      if (pad && useSpaces) {
1114
0
        toInsertInto.insert(_pos + getPos(), gSpace);
1115
0
      } else {
1116
0
        pad = true;
1117
0
      }
1118
0
      int64_t digit = dl.getDigit(didx);
1119
0
      getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status);
1120
0
    }
1121
1122
0
    if (!pad) {
1123
      // hack around lack of precision in digitlist. if we would end up with
1124
      // "foo point" make sure we add a " zero" to the end.
1125
0
      getRuleSet()->format(static_cast<int64_t>(0), toInsertInto, _pos + getPos(), recursionCount, status);
1126
0
    }
1127
0
  }
1128
0
}
1129
1130
//-----------------------------------------------------------------------
1131
// parsing
1132
//-----------------------------------------------------------------------
1133
1134
/**
1135
 * If in "by digits" mode, parses the string as if it were a string
1136
 * of individual digits; otherwise, uses the superclass function.
1137
 * @param text The string to parse
1138
 * @param parsePosition Ignored on entry, but updated on exit to point
1139
 * to the first unmatched character
1140
 * @param baseValue The partial parse result prior to entering this
1141
 * function
1142
 * @param upperBound Only consider rules with base values lower than
1143
 * this when filling in the substitution
1144
 * @param lenientParse If true, try matching the text as numerals if
1145
 * matching as words doesn't work
1146
 * @return If the match was successful, the current partial parse
1147
 * result; otherwise new Long(0).  The result is either a Long or
1148
 * a Double.
1149
 */
1150
1151
UBool
1152
FractionalPartSubstitution::doParse(const UnicodeString& text,
1153
                ParsePosition& parsePosition,
1154
                double baseValue,
1155
                double /*upperBound*/,
1156
                UBool lenientParse,
1157
                uint32_t nonNumericalExecutedRuleMask,
1158
                int32_t recursionCount,
1159
                Formattable& resVal) const
1160
38.8k
{
1161
    // if we're not in byDigits mode, we can just use the inherited
1162
    // doParse()
1163
38.8k
    if (!byDigits) {
1164
1.46k
        return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, recursionCount, resVal);
1165
1166
        // if we ARE in byDigits mode, parse the text one digit at a time
1167
        // using this substitution's owning rule set (we do this by setting
1168
        // upperBound to 10 when calling doParse() ) until we reach
1169
        // nonmatching text
1170
37.3k
    } else {
1171
37.3k
        UnicodeString workText(text);
1172
37.3k
        ParsePosition workPos(1);
1173
37.3k
        double result = 0;
1174
37.3k
        int32_t digit;
1175
//          double p10 = 0.1;
1176
1177
37.3k
        DecimalQuantity dl;
1178
37.3k
        int32_t totalDigits = 0;
1179
37.3k
        NumberFormat* fmt = nullptr;
1180
107k
        while (workText.length() > 0 && workPos.getIndex() != 0) {
1181
69.9k
            workPos.setIndex(0);
1182
69.9k
            Formattable temp;
1183
69.9k
            getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, recursionCount, temp);
1184
69.9k
            UErrorCode status = U_ZERO_ERROR;
1185
69.9k
            digit = temp.getLong(status);
1186
//            digit = temp.getType() == Formattable::kLong ?
1187
//               temp.getLong() :
1188
//            (int32_t)temp.getDouble();
1189
1190
69.9k
            if (lenientParse && workPos.getIndex() == 0) {
1191
0
                if (!fmt) {
1192
0
                    status = U_ZERO_ERROR;
1193
0
                    fmt = NumberFormat::createInstance(status);
1194
0
                    if (U_FAILURE(status)) {
1195
0
                        delete fmt;
1196
0
                        fmt = nullptr;
1197
0
                    }
1198
0
                }
1199
0
                if (fmt) {
1200
0
                    fmt->parse(workText, temp, workPos);
1201
0
                    digit = temp.getLong(status);
1202
0
                }
1203
0
            }
1204
1205
69.9k
            if (workPos.getIndex() != 0) {
1206
33.1k
                dl.appendDigit(static_cast<int8_t>(digit), 0, true);
1207
33.1k
                totalDigits++;
1208
//                  result += digit * p10;
1209
//                  p10 /= 10;
1210
33.1k
                parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1211
33.1k
                workText.removeBetween(0, workPos.getIndex());
1212
37.9k
                while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1213
4.86k
                    workText.removeBetween(0, 1);
1214
4.86k
                    parsePosition.setIndex(parsePosition.getIndex() + 1);
1215
4.86k
                }
1216
33.1k
            }
1217
69.9k
        }
1218
37.3k
        delete fmt;
1219
1220
37.3k
        dl.adjustMagnitude(-totalDigits);
1221
37.3k
        result = dl.toDouble();
1222
37.3k
        result = composeRuleValue(result, baseValue);
1223
37.3k
        resVal.setDouble(result);
1224
37.3k
        return true;
1225
37.3k
    }
1226
38.8k
}
1227
1228
bool
1229
FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
1230
0
{
1231
0
  return NFSubstitution::operator==(rhs) &&
1232
0
  ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
1233
0
}
1234
1235
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution)
1236
1237
1238
//===================================================================
1239
// AbsoluteValueSubstitution
1240
//===================================================================
1241
1242
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution)
1243
1244
//===================================================================
1245
// NumeratorSubstitution
1246
//===================================================================
1247
1248
void
1249
0
NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const {
1250
0
    if (U_FAILURE(status)) return;
1251
    // perform a transformation on the number being formatted that
1252
    // is dependent on the type of substitution this is
1253
1254
0
    double numberToFormat = transformNumber(number);
1255
0
    int64_t longNF = util64_fromDouble(numberToFormat);
1256
1257
0
    const NFRuleSet* aruleSet = getRuleSet();
1258
0
    if (withZeros && aruleSet != nullptr) {
1259
        // if there are leading zeros in the decimal expansion then emit them
1260
0
        int64_t nf =longNF;
1261
0
        int32_t len = toInsertInto.length();
1262
0
        while ((nf *= 10) < denominator) {
1263
0
            toInsertInto.insert(apos + getPos(), gSpace);
1264
0
            aruleSet->format(static_cast<int64_t>(0), toInsertInto, apos + getPos(), recursionCount, status);
1265
0
        }
1266
0
        apos += toInsertInto.length() - len;
1267
0
    }
1268
1269
    // if the result is an integer, from here on out we work in integer
1270
    // space (saving time and memory and preserving accuracy)
1271
0
    if (numberToFormat == longNF && aruleSet != nullptr) {
1272
0
        aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status);
1273
1274
        // if the result isn't an integer, then call either our rule set's
1275
        // format() method or our DecimalFormat's format() method to
1276
        // format the result
1277
0
    } else {
1278
0
        if (aruleSet != nullptr) {
1279
0
            aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status);
1280
0
        } else {
1281
0
            UnicodeString temp;
1282
0
            getNumberFormat()->format(numberToFormat, temp, status);
1283
0
            toInsertInto.insert(apos + getPos(), temp);
1284
0
        }
1285
0
    }
1286
0
}
1287
1288
UBool 
1289
NumeratorSubstitution::doParse(const UnicodeString& text, 
1290
                               ParsePosition& parsePosition,
1291
                               double baseValue,
1292
                               double upperBound,
1293
                               UBool /*lenientParse*/,
1294
                               uint32_t nonNumericalExecutedRuleMask,
1295
                               int32_t recursionCount,
1296
                               Formattable& result) const
1297
275k
{
1298
    // we don't have to do anything special to do the parsing here,
1299
    // but we have to turn lenient parsing off-- if we leave it on,
1300
    // it SERIOUSLY messes up the algorithm
1301
1302
    // if withZeros is true, we need to count the zeros
1303
    // and use that to adjust the parse result
1304
275k
    UErrorCode status = U_ZERO_ERROR;
1305
275k
    int32_t zeroCount = 0;
1306
275k
    UnicodeString workText(text);
1307
1308
275k
    if (withZeros) {
1309
136k
        ParsePosition workPos(1);
1310
136k
        Formattable temp;
1311
1312
144k
        while (workText.length() > 0 && workPos.getIndex() != 0) {
1313
144k
            workPos.setIndex(0);
1314
144k
            getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, recursionCount, temp); // parse zero or nothing at all
1315
144k
            if (workPos.getIndex() == 0) {
1316
                // we failed, either there were no more zeros, or the number was formatted with digits
1317
                // either way, we're done
1318
136k
                break;
1319
136k
            }
1320
1321
7.35k
            ++zeroCount;
1322
7.35k
            parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1323
7.35k
            workText.remove(0, workPos.getIndex());
1324
9.17k
            while (workText.length() > 0 && workText.charAt(0) == gSpace) {
1325
1.81k
                workText.remove(0, 1);
1326
1.81k
                parsePosition.setIndex(parsePosition.getIndex() + 1);
1327
1.81k
            }
1328
7.35k
        }
1329
1330
136k
        workText = text;
1331
136k
        workText.remove(0, parsePosition.getIndex());
1332
136k
        parsePosition.setIndex(0);
1333
136k
    }
1334
1335
    // we've parsed off the zeros, now let's parse the rest from our current position
1336
275k
    NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, recursionCount, result);
1337
1338
275k
    if (withZeros) {
1339
        // any base value will do in this case.  is there a way to
1340
        // force this to not bother trying all the base values?
1341
1342
        // compute the 'effective' base and prescale the value down
1343
136k
        int64_t n = result.getLong(status); // force conversion!
1344
136k
        int64_t d = 1;
1345
136k
        while (d <= n) {
1346
0
            d *= 10;
1347
0
        }
1348
        // now add the zeros
1349
144k
        while (zeroCount > 0) {
1350
7.35k
            d *= 10;
1351
7.35k
            --zeroCount;
1352
7.35k
        }
1353
        // d is now our true denominator
1354
136k
        result.setDouble(static_cast<double>(n) / static_cast<double>(d));
1355
136k
    }
1356
1357
275k
    return true;
1358
275k
}
1359
1360
bool
1361
NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
1362
0
{
1363
0
    return NFSubstitution::operator==(rhs) &&
1364
0
        denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
1365
0
}
1366
1367
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution)
1368
1369
const char16_t NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c };
1370
        
1371
U_NAMESPACE_END
1372
1373
/* U_HAVE_RBNF */
1374
#endif
1375